diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp index 5e2cc207560d3..45274ac61f8b3 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp @@ -257,6 +257,13 @@ void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( SourceLocation Loc) { parseToLocation(Loc); } +/*TO_UPSTREAM(BoundsSafety) ON*/ +void ExpandModularHeadersPPCallbacks:: +PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) { + parseToLocation(Loc); +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h index 0742c21bc4372..fbc0c17cdd24a 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h @@ -99,6 +99,10 @@ class ExpandModularHeadersPPCallbacks : public PPCallbacks { void PragmaWarningPop(SourceLocation Loc) override; void PragmaAssumeNonNullBegin(SourceLocation Loc) override; void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + /*TO_UPSTREAM(BoundsSafety) ON*/ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef) override; + /*TO_UPSTREAM(BoundsSafety) OFF*/ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, const MacroArgs *) override; void MacroDefined(const Token &MacroNameTok, diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 42efc64ce87eb..4e52d5886bf3c 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -1691,7 +1691,17 @@ enum CXCursorKind { */ CXCursor_PackIndexingExpr = 156, - CXCursor_LastExpr = CXCursor_PackIndexingExpr, + /* TO_UPSTREAM(BoundsSafety) ON */ + /** + * BoundsSafety '__builtin_unsafe_forge_bidi_indexable(addr, size)' or + * '__builtin_forge_single(addr) + */ + CXCursor_ForgePtrExpr = 198, + + CXCursor_GetBoundExpr = 199, + + CXCursor_LastExpr = CXCursor_GetBoundExpr, + /* TO_UPSTREAM(BoundsSafety) OFF */ /* Statements */ CXCursor_FirstStmt = 200, diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index b66d0d1da2192..8b38be6b3b932 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -300,6 +300,72 @@ inline bool operator!=(const ContextInfo &LHS, const ContextInfo &RHS) { return !(LHS == RHS); } +/* TO_UPSTREAM(BoundsSafety) ON */ +class BoundsSafetyInfo { +public: + enum class BoundsSafetyKind { + CountedBy = 0, + CountedByOrNull, + SizedBy, + SizedByOrNull, + EndedBy, + }; + +private: + /// Whether this property has been audited for nullability. + LLVM_PREFERRED_TYPE(bool) + unsigned KindAudited : 1; + + /// The kind of nullability for this property. Only valid if the nullability + /// has been audited. + LLVM_PREFERRED_TYPE(BoundsSafetyKind) + unsigned Kind : 3; + + LLVM_PREFERRED_TYPE(bool) + unsigned LevelAudited : 1; + + unsigned Level : 3; + +public: + std::string ExternalBounds; + + BoundsSafetyInfo() + : KindAudited(false), Kind(0), LevelAudited(false), Level(0), + ExternalBounds("") {} + + std::optional getKind() const { + return KindAudited ? std::optional( + static_cast(Kind)) + : std::nullopt; + } + + void setKindAudited(BoundsSafetyKind kind) { + KindAudited = true; + Kind = static_cast(kind); + } + + std::optional getLevel() const { + return LevelAudited ? std::optional(Level) : std::nullopt; + } + + void setLevelAudited(unsigned level) { + LevelAudited = true; + Level = level; + } + + friend bool operator==(const BoundsSafetyInfo &, const BoundsSafetyInfo &); + + LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS) const; +}; + +inline bool operator==(const BoundsSafetyInfo &LHS, + const BoundsSafetyInfo &RHS) { + return LHS.KindAudited == RHS.KindAudited && LHS.Kind == RHS.Kind && + LHS.LevelAudited == RHS.LevelAudited && LHS.Level == RHS.Level && + LHS.ExternalBounds == RHS.ExternalBounds; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// API notes for a variable/property. class VariableInfo : public CommonEntityInfo { /// Whether this property has been audited for nullability. @@ -439,10 +505,14 @@ class ParamInfo : public VariableInfo { unsigned RawRetainCountConvention : 3; public: + /* TO_UPSTREAM(BoundsSafety) ON */ + std::optional BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + ParamInfo() : NoEscapeSpecified(false), NoEscape(false), LifetimeboundSpecified(false), Lifetimebound(false), - RawRetainCountConvention() {} + RawRetainCountConvention(), BoundsSafety(std::nullopt) {} std::optional isNoEscape() const { return NoEscapeSpecified ? std::optional(NoEscape) : std::nullopt; @@ -488,6 +558,11 @@ class ParamInfo : public VariableInfo { if (!RawRetainCountConvention) RawRetainCountConvention = RHS.RawRetainCountConvention; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (!BoundsSafety) + BoundsSafety = RHS.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + return *this; } @@ -502,7 +577,10 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) { LHS.NoEscape == RHS.NoEscape && LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified && LHS.Lifetimebound == RHS.Lifetimebound && - LHS.RawRetainCountConvention == RHS.RawRetainCountConvention; + LHS.RawRetainCountConvention == RHS.RawRetainCountConvention && + /* TO_UPSTREAM(BoundsSafety) ON */ + LHS.BoundsSafety == RHS.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ } inline bool operator!=(const ParamInfo &LHS, const ParamInfo &RHS) { diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index c4206b73b1156..94b5abee1d276 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/CharUnits.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APFloat.h" @@ -29,7 +30,6 @@ template class BasicReaderBase; class AddrLabelExpr; class ASTContext; - class CharUnits; class CXXRecordDecl; class Decl; class DiagnosticBuilder; @@ -88,6 +88,59 @@ class DynamicAllocLValue { static constexpr int NumLowBitsAvailable = 3; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// Symbolic representation of a size forged pointer +class ForgedPtrLValue { + const ValueDecl *Base; + +public: + ForgedPtrLValue() : Base(nullptr) {} + explicit ForgedPtrLValue(const ValueDecl *Base) : Base(Base) {} + explicit operator bool() const { return Base; } + + const ValueDecl *getBaseValueDecl() const { return Base; } + + void *getOpaqueValue() { return const_cast(Base); } + static ForgedPtrLValue getFromOpaqueValue(void *Value) { + ForgedPtrLValue V; + V.Base = reinterpret_cast(Value); + return V; + } +}; + +/// This represents a pointer union that is either DynamicAllocLValue or +/// ForgedPtrLValue. +/// +/// This is a hack to be able to add ForgedPtrLValue as another pointer member +/// to the pointer union of APValue::LValueBase because it already has the +/// maximum number of members for the available low bits, i.e., 2. +/// DynamicAllocLValue and ValueDecl* have 3 low bits available and thus we can +/// use this one more remaining bit to encode ForgedPtrLValue. +class DynamicAllocOrForgedPtrLValue + : public llvm::PointerUnion { + + using BaseTy = llvm::PointerUnion; + +public: + DynamicAllocOrForgedPtrLValue() = default; + DynamicAllocOrForgedPtrLValue(DynamicAllocLValue LV) : BaseTy(LV) {} + DynamicAllocOrForgedPtrLValue(ForgedPtrLValue LV) : BaseTy(LV) {} + + explicit operator bool() const { + if (is()) { + return static_cast(get()); + } + return static_cast(get()); + } + + static DynamicAllocOrForgedPtrLValue getFromOpaqueValue(void *Value) { + DynamicAllocOrForgedPtrLValue V; + V.Val.setFromOpaqueValue(Value); + return V; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace llvm { @@ -113,6 +166,28 @@ template<> struct PointerLikeTypeTraits { static constexpr int NumLowBitsAvailable = clang::DynamicAllocLValue::NumLowBitsAvailable; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +template <> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::ForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::ForgedPtrLValue getFromVoidPointer(void *P) { + return clang::ForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 3; +}; + +template <> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::DynamicAllocOrForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::DynamicAllocOrForgedPtrLValue getFromVoidPointer(void *P) { + return clang::DynamicAllocOrForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 2; +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace clang { @@ -145,28 +220,64 @@ class APValue { class LValueBase { typedef llvm::PointerUnion + DynamicAllocOrForgedPtrLValue> PtrTy; + template + static constexpr bool IsDAOrForgedV = + std::is_same::value || + std::is_same::value; + + template + using EnableIfDAOrForged = + typename std::enable_if, U>::type; + + template + using EnableIfNotDANorForged = + typename std::enable_if, U>::type; + public: LValueBase() : Local{} {} LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0); LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0); static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type); static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); + static LValueBase getForgedPtr(ForgedPtrLValue LV, QualType Type); void Profile(llvm::FoldingSetNodeID &ID) const; - template - bool is() const { return Ptr.is(); } + template EnableIfNotDANorForged is() const { + return Ptr.is(); + } - template - T get() const { return Ptr.get(); } + template EnableIfDAOrForged is() const { + if (!Ptr.is()) + return false; + return Ptr.get().is(); + } - template - T dyn_cast() const { return Ptr.dyn_cast(); } + template EnableIfNotDANorForged get() const { + return Ptr.get(); + } + + template EnableIfDAOrForged get() const { + assert(is() && "Invalid accessor called"); + return Ptr.get().get(); + } + + template EnableIfNotDANorForged dyn_cast() const { + return Ptr.dyn_cast(); + } + + template EnableIfDAOrForged dyn_cast() const { + if (is()) + return Ptr.get().dyn_cast(); + return T(); + } void *getOpaqueValue() const; + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *getValueDecl() const; bool isNull() const; @@ -176,6 +287,7 @@ class APValue { unsigned getVersion() const; QualType getTypeInfoType() const; QualType getDynamicAllocType() const; + QualType getForgedPtrAsArrayType() const; QualType getType() const; @@ -197,6 +309,8 @@ class APValue { void *TypeInfoType; /// The QualType, if this is a DynamicAllocLValue. void *DynamicAllocType; + /// The QualType, if this is a ForgedPtrLValue. + void *ForgedPtrAsArrayType; }; }; @@ -305,10 +419,45 @@ class APValue { }; struct MemberPointerData; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LVBase { + APValue::LValueBase Base; + CharUnits Offset; + // BoundsSafety : While the base also holds a corresponding constant array type + // for forged pointer, we still keep track of forged size because the array + // size will be different from the actual forged size if it is not a multiple + // of element type size after a bitcast. The codegen doesn't round up/down + // the bounds to be a type-size multiple, we should keep it the same for + // constant emission. Once __builtin_forge_* has a type as an argument, we + // may consider round down the size with the element type size. + CharUnits ForgedSize; + // While 'Offset' is the offset within the LValue, 'ForgedOffset' is the + // offset of the base pointer of __builtin_unsafe_forge*. For example, in + // the following, + // '__bidi_indexable_unsafe_forge_bidi_indexable(base + N) + M' + // 'N' should be 'ForgedOffset' and 'M' should be 'Offset'. This way, the + // forged pointer itself becomes an LValue starting at base + 'ForgedOffset'. + CharUnits ForgedOffset; + unsigned PathLength; + bool IsNullPtr : 1; + bool IsOnePastTheEnd : 1; + bool IsForgeBidi : 1; + bool IsForgeSingle : 1; + bool IsForgeTerminatedBy : 1; + }; + + struct LVPlaceHolder { + LVBase Base; + LValuePathEntry Path[1]; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // We ensure elsewhere that Data is big enough for LV and MemberPointerData. typedef llvm::AlignedCharArrayUnion DataType; + UnionData, AddrLabelDiffData, + // TO_UPSTREAM(BoundsSafety) + LVPlaceHolder> DataType; static const size_t DataSize = sizeof(DataType); DataType Data; @@ -487,12 +636,33 @@ class APValue { const CharUnits &getLValueOffset() const { return const_cast(this)->getLValueOffset(); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + CharUnits &getLValueForgedSize(); + const CharUnits &getLValueForgedSize() const { + return const_cast(this)->getLValueForgedSize(); + } + + CharUnits &getLValueForgedOffset(); + const CharUnits &getLValueForgedOffset() const { + return const_cast(this)->getLValueForgedOffset(); + } + + CharUnits getUnwrappedLValueOffset() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + bool isLValueOnePastTheEnd() const; bool hasLValuePath() const; ArrayRef getLValuePath() const; unsigned getLValueCallIndex() const; unsigned getLValueVersion() const; bool isNullPointer() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isLValueForgeBidi() const; + bool isLValueForgeSingle() const; + bool isLValueForgeTerminatedBy() const; + bool isLValueForge() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ APValue &getVectorElt(unsigned I) { assert(isVector() && "Invalid accessor"); @@ -628,6 +798,12 @@ class APValue { ((AddrLabelDiffData *)(char *)&Data)->RHSExpr = RHSExpr; } + /* TO_UPSTREAM(BoundsSafety) ON */ + void setLValueForgedBidi(const CharUnits &Size, const CharUnits &Offset); + void setLValueForgedSingle(const CharUnits &Offset); + void setLValueForgedTerminatedBy(const CharUnits &Offset); + /* TO_UPSTREAM(BoundsSafety) OFF */ + private: void DestroyDataAndMakeUninit(); void MakeInt() { diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 29773873eef59..0c5ba11e72c71 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -256,6 +256,14 @@ class ASTContext : public RefCountedBase { mutable llvm::FoldingSet CountAttributedTypes; + /* TO_UPSTREAM(BoundsSafety) ON */ + mutable llvm::FoldingSet + DynamicRangePointerTypes; + + mutable llvm::ContextualFoldingSet + ValueTerminatedTypes; + /* TO_UPSTREAM(BoundsSafety) OFF */ + mutable llvm::FoldingSet QualifiedTemplateNames; mutable llvm::FoldingSet DependentTemplateNames; mutable llvm::FoldingSet @@ -1404,9 +1412,12 @@ class ASTContext : public RefCountedBase { /// Return the uniqued reference to the type for a pointer to /// the specified type. - QualType getPointerType(QualType T) const; - CanQualType getPointerType(CanQualType T) const { - return CanQualType::CreateUnsafe(getPointerType((QualType) T)); + QualType getPointerType(QualType T, BoundsSafetyPointerAttributes A = + BoundsSafetyPointerAttributes()) const; + CanQualType getPointerType( + CanQualType T, + BoundsSafetyPointerAttributes A = BoundsSafetyPointerAttributes()) const { + return CanQualType::CreateUnsafe(getPointerType((QualType)T, A)); } QualType @@ -1414,6 +1425,42 @@ class ASTContext : public RefCountedBase { bool OrNull, ArrayRef DependentDecls) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + QualType getDynamicRangePointerType( + QualType T, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) const; + + QualType getValueTerminatedType(QualType T, Expr *TerminatorExpr) const; + + /// Return a new pointer type with the -fbounds-safety pointer attribute with + /// preserving existing AttributedTypes and qualifiers. + QualType getBoundsSafetyPointerType(QualType PointerTy, + BoundsSafetyPointerAttributes); + + /// Return a result type of merging -fbounds-safety pointer attributes of \p SrcTy + /// to \p DstTy, while preserving existing AttributedTypes and qualifiers of + /// \p DstTy. The type merging is performed recursively in nested pointers. + /// The caller should provide \p MergeFunctor to create a merged pointer type + /// using the recursively merged pointee type. + /// mergeBoundsSafetyPointerTypes removes any AttributedType(s) from \p + /// DstTy, calls \p MergeFunctor to merge the attributes at each level, and + /// then reapplies the AttributedType(s) to the merged type. \p OrigDstTy is + /// the same as \p DstTy but without dropping the AttributedType(s). This + /// allows us to check any AttributedType(s) in \p MergeFunctor in order to + /// make decision about the merged type. + QualType mergeBoundsSafetyPointerTypes( + QualType DstTy, QualType SrcTy, + std::function &MergeFunctor, + QualType OrigDstTy = QualType()); + + QualType getBoundsSafetyAutoPointerType(QualType T, + BoundsSafetyPointerAttributes AbiFAttr, + bool ShouldAutoBound); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Return the uniqued reference to a type adjusted from the original /// type to a new type. QualType getAdjustedType(QualType Orig, QualType New) const; @@ -2409,6 +2456,14 @@ class ASTContext : public RefCountedBase { uint64_t getTypeSize(QualType T) const { return getTypeInfo(T).Width; } uint64_t getTypeSize(const Type *T) const { return getTypeInfo(T).Width; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + uint64_t getTypeSizeOrNull(QualType T) const { + if (T->isIncompleteOrSizelessType()) + return 0; + return getTypeSize(T); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return the size of the character type, in bits. uint64_t getCharWidth() const { return getTypeSize(CharTy); @@ -2663,6 +2718,37 @@ class ASTContext : public RefCountedBase { return getCanonicalType(T1) == getCanonicalType(T2); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// These enum values (other than CanMerge) are aligned with the options for + /// the third parameter in + /// diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch + enum BoundsSafePointerTypeMergeKind { + BSPTMK_NestedBoundsMismatch, + BSPTMK_FunctionTypeMismatch, + BSPTMK_TerminatedByMismatch, + BSPTMK_CanMerge, + }; + /// Given two pointer types that can be unified without losing type safety, + /// check whether they can be merged without losing bounds safety. + /// In practice this means checking whether their inner pointer type bounds + /// sugar nodes match (if any exist). Non-nested pointer types can always be + /// unified, potentially requiring dynamic checks. + BoundsSafePointerTypeMergeKind canMergeTypeBounds(QualType LHSTy, + QualType RHSTy) const; + + /// Given two pointer types, check whether either of them if is a + /// ValueTerminatedType, and if so, that the other is a ValueTerminatedType + /// with the same terminator. Does not check pointee type! + BoundsSafePointerTypeMergeKind + checkTerminatedByMismatch(QualType LHSTy, QualType RHSTy) const; + BoundsSafePointerTypeMergeKind canMergeInnerTypeBounds(QualType LHSTy, + QualType RHSTy) const; + BoundsSafePointerTypeMergeKind + canMergeFunctionTypeBounds(const FunctionProtoType *LHSTy, + const FunctionProtoType *RHSTy) const; + + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Determine whether the given expressions \p X and \p Y are equivalent. bool hasSameExpr(const Expr *X, const Expr *Y) const; @@ -2740,6 +2826,16 @@ class ASTContext : public RefCountedBase { /// Determine if two types are similar, ignoring only CVR qualifiers. bool hasCvrSimilarType(QualType T1, QualType T2); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Determine if two types have same -fbounds-safety pointer layouts, recursively. + bool hasSameBoundsSafetyPointerLayout(QualType T1, QualType T2); + + /// Determine if two types have compatible -fbounds-safety pointer layouts, + /// recursively. + bool hasCompatibleBoundsSafetyPointerLayout(QualType T1, QualType T2, + bool ExactCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Retrieves the "canonical" nested name specifier for a /// given nested name specifier. /// diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h index 2c4ec2ce67f36..b8bd5403df477 100644 --- a/clang/include/clang/AST/ASTMutationListener.h +++ b/clang/include/clang/AST/ASTMutationListener.h @@ -149,6 +149,17 @@ class ASTMutationListener { virtual void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) {} + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// An attribute to a Decl to write in a separate record because the attribute + /// and the Decl create a cycle during deserialization. + /// + /// \param Attr The attribute to the Decl + /// + /// \param Record The Decl owns the attribute + virtual void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) {} + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// The parser find the named module declaration. virtual void EnteringModulePurview() {} diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index 4b627c65e276b..0cc78f56cc903 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -219,6 +219,13 @@ class DataStreamBasicReader : public BasicReaderBase { return Qualifiers::fromOpaqueValue(value); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + uint32_t value = asImpl().readUInt32(); + return BoundsSafetyPointerAttributes::fromOpaqueValue(value); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + FunctionProtoType::ExceptionSpecInfo readExceptionSpecInfo(llvm::SmallVectorImpl &buffer) { FunctionProtoType::ExceptionSpecInfo esi; diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index b941add8bde88..79c7fb9fa1a2d 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -201,6 +201,12 @@ class DataStreamBasicWriter : public BasicWriterBase { asImpl().writeUInt64(value.getAsOpaqueValue()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void writeBoundsSafetyPointerAttributes(BoundsSafetyPointerAttributes value) { + asImpl().writeUInt32(value.getAsOpaqueValue()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void writeExceptionSpecInfo( const FunctionProtoType::ExceptionSpecInfo &esi) { asImpl().writeUInt32(uint32_t(esi.Type)); diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 152dc2ffe7e3f..269c8a1174e72 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -380,6 +380,13 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, DB.AddTaggedVal(reinterpret_cast(At), DiagnosticsEngine::ak_attr); return DB; } + +/// Determine if type T is a valid subject for a nonnull and similar +/// attributes. Dependent types are considered valid so they can be checked +/// during instantiation time. By default, we look through references (the +/// behavior used by nonnull), but if the second parameter is true, then we +/// treat a reference type as valid. +bool isValidPointerAttrType(QualType T, bool RefOkay = false); } // end namespace clang #endif diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 6d3a51c379f9d..2ae54f2c762c8 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -79,6 +79,15 @@ class LambdaExpr; class CXXUnresolvedConstructExpr; class CXXDependentScopeMemberExpr; class MaterializeTemporaryExpr; +/* TO_UPSTREAM(BoundsSafety) ON */ +class MaterializeSequenceExpr; +class PredefinedBoundsCheckExpr; +class BoundsCheckExpr; +class AssumptionExpr; +class BoundsSafetyPointerPromotionExpr; +class GetBoundExpr; +class ForgePtrExpr; +/* TO_UPSTREAM(BoundsSafety) OFF */ class CXXFoldExpr; class CXXParenListInitExpr; class TypeTraitExpr; @@ -176,6 +185,16 @@ ExprDependence computeDependence(TypeTraitExpr *E); ExprDependence computeDependence(ConceptSpecializationExpr *E, bool ValueDependent); +/* TO_UPSTREAM(BoundsSafety) ON */ +ExprDependence computeDependence(MaterializeSequenceExpr *E); +ExprDependence computeDependence(PredefinedBoundsCheckExpr *E); +ExprDependence computeDependence(BoundsCheckExpr *E); +ExprDependence computeDependence(AssumptionExpr *E); +ExprDependence computeDependence(BoundsSafetyPointerPromotionExpr *E); +ExprDependence computeDependence(GetBoundExpr *E); +ExprDependence computeDependence(ForgePtrExpr *E); +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprDependence computeDependence(SYCLUniqueStableNameExpr *E); ExprDependence computeDependence(PredefinedExpr *E); ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef PreArgs); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 561a9d872acfb..50a712836e3dd 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -687,6 +687,14 @@ class ValueDecl : public NamedDecl { /// can be captured. bool isInitCapture() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Whether this decl is a dependent parameter referred to by the return type + /// that is a bounds-attributed type. + bool isDependentParamOfReturnType( + const BoundsAttributedType **RetType = nullptr, + const TypeCoupledDeclRefInfo **Info = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // If this is a VarDecl, or a BindindDecl with an // associated decomposed VarDecl, return that VarDecl. VarDecl *getPotentiallyDecomposedVarDecl(); @@ -4338,6 +4346,9 @@ class RecordDecl : public TagDecl { /// leaks. bool isOrContainsUnion() const; + // TO_UPSTREAM(BoundsSafety) + bool isParentStructOf(const Decl *D) const; + // Iterator access to field members. The field iterator only visits // the non-static data members of this class, ignoring any static // data members, functions, constructors, destructors, etc. @@ -5003,6 +5014,12 @@ void Redeclarable::setPreviousDecl(decl_type *PrevDecl) { cast(static_cast(this))->isLinkageValid()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +// A BoundsSafety helper function. +/// Return `true` if \p D is const qualified or attributed as immutable. +bool IsConstOrLateConst(const Decl *D); +/* TO_UPSTREAM(BoundsSafety) OFF */ + // Inline function definitions. /// Check if the given decl is complete. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 5b813bfc2faf9..7857b2c37fd0a 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -31,6 +31,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" @@ -262,6 +263,12 @@ class Expr : public ValueStmt { bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx, + llvm::SmallPtrSetImpl &B) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ /// isLValue - True if this expression is an "l-value" according to /// the rules of the current language. C and C++ give somewhat @@ -782,6 +789,18 @@ class Expr : public ValueStmt { /// strlen, false otherwise. bool tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// If the current Expr is an array or a pointer to an array element, this + /// will try to statically determine the value of the last element of that + /// array. + bool tryEvaluateTerminatorElement(EvalResult &Result, + const ASTContext &Ctx) const; + + bool EvaluateAsTerminatorValue( + llvm::APSInt &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, @@ -837,6 +856,11 @@ class Expr : public ValueStmt { ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + NullPointerConstantKind isNullPointerConstantIgnoreCastsAndOVEs( + ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// isOBJCGCCandidate - Return true if this expression may be used in a read/ /// write barrier. bool isOBJCGCCandidate(ASTContext &Ctx) const; @@ -919,6 +943,8 @@ class Expr : public ValueStmt { /// * What IgnoreImpCasts() skips /// * MaterializeTemporaryExpr /// * SubstNonTypeTemplateParmExpr + // TO_UPSTREAM(BoundsSafety) + Expr *IgnoreParenImpCasts(llvm::SmallPtrSetImpl &BoundValues) LLVM_READONLY; Expr *IgnoreParenImpCasts() LLVM_READONLY; const Expr *IgnoreParenImpCasts() const { return const_cast(this)->IgnoreParenImpCasts(); @@ -1175,6 +1201,13 @@ class OpaqueValueExpr : public Expr { Expr *SourceExpr; public: + /*TO_UPSTREAM(BoundsSafety) ON*/ + static OpaqueValueExpr *Wrap(const ASTContext &Context, Expr *E); + static OpaqueValueExpr *EnsureWrapped( + const ASTContext &Context, Expr *E, + SmallVectorImpl &OVEs); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + OpaqueValueExpr(SourceLocation Loc, QualType T, ExprValueKind VK, ExprObjectKind OK = OK_Ordinary, Expr *SourceExpr = nullptr) : Expr(OpaqueValueExprClass, T, VK, OK), SourceExpr(SourceExpr) { @@ -3839,6 +3872,832 @@ class CStyleCastExpr final friend class CastExpr; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +// Attaches one or more assumptions to an expression. The assumptions are each +// codegen'd like they were the parameter of `__builtin_assume`. +class AssumptionExpr final + : public Expr, + private llvm::TrailingObjects { + friend TrailingObjects; + friend class ASTStmtWriter; + + Expr **getTrailingExprs() { + return const_cast(getTrailingObjects()); + } + + Expr *const *getTrailingExprs() const { + return getTrailingObjects(); + } + + AssumptionExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(AssumptionExprClass, Empty) { + AssumptionExprBits.NumExprs = NumExprs; + } + + AssumptionExpr(Expr *ResultExpr, llvm::ArrayRef Assumptions); + +public: + static bool classof(const Stmt *T) { + return T->getStmtClass() == AssumptionExprClass; + } + + static AssumptionExpr *Create(const ASTContext &Ctx, Expr *Result, + llvm::ArrayRef Assumptions); + + static AssumptionExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + Expr *getWrappedExpr() { return getTrailingExprs()[0]; } + const Expr *getWrappedExpr() const { return getTrailingExprs()[0]; } + + void setWrappedExpr(Expr *E) { + if (E) { + getTrailingExprs()[0] = E; + setType(E->getType()); + setValueKind(E->getValueKind()); + setObjectKind(E->getObjectKind()); + } else { + setType(QualType()); + } + } + + unsigned getNumSubExprs() const { return AssumptionExprBits.NumExprs; } + Expr *getSubExpr(unsigned I) { return getTrailingExprs()[I]; } + const Expr *getSubExpr(unsigned I) const { return getTrailingExprs()[I]; } + void setSubExpr(unsigned I, Expr *E) { + if (I == 0) + setWrappedExpr(E); + else + getTrailingExprs()[I] = E; + } + + unsigned getNumAssumptions() const { return getNumSubExprs() - 1; } + Expr *getAssumption(unsigned I) { return getSubExpr(I + 1); } + const Expr *getAssumption(unsigned I) const { return getSubExpr(I + 1); } + void setAssumption(unsigned I, Expr *E) { setSubExpr(I + 1, E); } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getWrappedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getWrappedExpr()->getEndLoc(); + } + + typedef Expr * const * assumption_iterator; + typedef const Expr * const * const_assumption_iterator; + + assumption_iterator assumptions_begin() { + return reinterpret_cast (getTrailingExprs()) + 1; + } + const_assumption_iterator assumptions_begin() const { + return reinterpret_cast (getTrailingExprs()) + 1; + } + assumption_iterator assumptions_end() { + return assumptions_begin() + getNumAssumptions(); + } + const_assumption_iterator assumptions_end() const { + return assumptions_begin() + getNumAssumptions(); + } + + llvm::iterator_range assumptions() { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + llvm::iterator_range assumptions() const { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + + child_range children() { + Stmt **begin = reinterpret_cast(getTrailingExprs()); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast(getTrailingExprs()); + return const_child_range(begin, begin + getNumSubExprs()); + } +}; + +// Implicitly promote a pointer with external bounds to a wide pointer. Although +// this expression doesn't belong to the CastExpr family, it should usually be +// treated as such. +class BoundsSafetyPointerPromotionExpr final + : public Expr, + private llvm::TrailingObjects { + friend TrailingObjects; + friend class ASTStmtWriter; + + BoundsSafetyPointerPromotionExpr(EmptyShell Empty) + : Expr(BoundsSafetyPointerPromotionExprClass, Empty) { + setPointer(nullptr); + setNullCheck(false); + } + + BoundsSafetyPointerPromotionExpr(QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound, bool NullCheck = false) + : Expr(BoundsSafetyPointerPromotionExprClass, QT, VK_PRValue, OK_Ordinary) { + setPointer(Ptr); + setNullCheck(NullCheck); + if (Stmt **lowerPtr = getLowerBoundPtr()) + *lowerPtr = LowerBound; + if (Stmt **upperPtr = getUpperBoundPtr()) + *upperPtr = UpperBound; + setDependence(computeDependence(this)); + } + + BoundsSafetyPointerPromotionExpr *unconst() const { + return const_cast(this); + } + + Stmt **getPointerPtr(); + Stmt **getLowerBoundPtr(); + Stmt **getUpperBoundPtr(); + +public: + static BoundsSafetyPointerPromotionExpr * + Create(const ASTContext &Context, QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound = nullptr, bool NullCheck = false); + + static BoundsSafetyPointerPromotionExpr *CreateEmpty(const ASTContext &Context, + unsigned SubExprCount); + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getPointer()->getBeginLoc(); + } + + SourceLocation getEndLoc() const LLVM_READONLY { + return getPointer()->getEndLoc(); + } + + Expr *getSubExprAsWritten(); + Expr *getPointer() { return cast_or_null(*getPointerPtr()); } + Expr *getLowerBound() { return cast_or_null(*getLowerBoundPtr()); } + Expr *getUpperBound() { return cast_or_null(*getUpperBoundPtr()); } + const Expr *getSubExpr() const { return getPointer(); } + const Expr *getPointer() const { return unconst()->getPointer(); } + const Expr *getLowerBound() const { return unconst()->getLowerBound(); } + const Expr *getUpperBound() const { return unconst()->getUpperBound(); } + bool getNullCheck() const { + return BoundsSafetyPointerPromotionExprBits.NullCheck; + } + + Expr *getSubExpr() { return getPointer(); } + const Expr *getSubExprAsWritten() const { + return unconst()->getSubExprAsWritten(); + } + + void setPointer(Expr *newValue) { *getPointerPtr() = newValue; } + void setLowerBound(Expr *newValue) { *getLowerBoundPtr() = newValue; } + void setUpperBound(Expr *newValue) { *getUpperBoundPtr() = newValue; } + void setNullCheck(bool NullCheck) { + BoundsSafetyPointerPromotionExprBits.NullCheck = NullCheck; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsSafetyPointerPromotionExprClass; + } + + unsigned getNumChildren() const { + return unconst()->getLowerBoundPtr() ? 3 : 2; + } + + child_range children() { + return child_range(getPointerPtr(), getPointerPtr() + getNumChildren()); + } + + const_child_range children() const { + auto Range = unconst()->children(); + return const_child_range(Range.begin(), Range.end()); + } +}; + +/// __unsafe_forge_bidi_indexable(addr, size) +/// __unsafe_forge_single(addr) +/// __unsafe_forge_terminated_by(addr, terminator) +class ForgePtrExpr final : public Expr { + enum { ADDR, SIZE, TERMINATOR, NUM_SUBEXPRS }; + + Stmt *SubExprs[NUM_SUBEXPRS]; + + SourceLocation KWLoc; + SourceLocation RParenLoc; + +public: + ForgePtrExpr(QualType T, ExprValueKind VK, Expr *AddrExpr, Expr *SizeExpr, + Expr *TermExpr, SourceLocation KWLoc, SourceLocation RParenLoc) + : Expr(ForgePtrExprClass, T, VK, OK_Ordinary), KWLoc(KWLoc), + RParenLoc(RParenLoc) { + SubExprs[ADDR] = AddrExpr; + SubExprs[SIZE] = SizeExpr; + SubExprs[TERMINATOR] = TermExpr; + setDependence(computeDependence(this)); + } + + explicit ForgePtrExpr(EmptyShell Empty) + : Expr(ForgePtrExprClass, Empty) { + SubExprs[ADDR] = nullptr; + SubExprs[SIZE] = nullptr; + SubExprs[TERMINATOR] = nullptr; + } + + bool ForgesBidiIndexablePointer() const { + return !ForgesSinglePointer() && !ForgesTerminatedByPointer(); + } + bool ForgesSinglePointer() const { + auto BaseFA = getType()->getAs()->getPointerAttributes(); + return !BaseFA.hasUpperBound() && !ForgesTerminatedByPointer(); + } + bool ForgesTerminatedByPointer() const { + return getType()->isValueTerminatedType(); + } + + Expr *getAddr() const { return cast(SubExprs[ADDR]); } + void setAddr(Expr *E) { SubExprs[ADDR] = E; } + Expr *getSize() const { return cast_or_null(SubExprs[SIZE]); } + void setSize(Expr *E) { SubExprs[SIZE] = E; } + Expr *getTerminator() const { + return cast_or_null(SubExprs[TERMINATOR]); + } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + SourceLocation getBeginLoc() const LLVM_READONLY { return KWLoc; } + void setBeginLoc(SourceLocation Loc) { KWLoc = Loc; } + SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } + void setEndLoc(SourceLocation Loc) { RParenLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ForgePtrExprClass; + } + + // Iterators + child_range children() { + return child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } + const_child_range children() const { + return const_child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } +}; + +/// GetBoundExpr - get the lower or upper bound of a pointer expression. +/// This would be a builtin if it wasn't easier to create an entirely new +/// expression subclass than to instantiate a call to a builtin from Sema. +/// Most GetBoundExpr are synthetic, and their source locations default to +class GetBoundExpr final : public Expr { +public: + enum BoundKind { BK_Lower, BK_Upper }; + +private: + Stmt *SubExpr; + BoundKind Kind; + SourceLocation BuiltinLoc, RParenLoc; + +public: + GetBoundExpr(SourceLocation BuiltinLoc, SourceLocation RParenLoc, + Expr *SubExpr, BoundKind Kind, QualType ResultType) + : Expr(GetBoundExprClass, ResultType, VK_PRValue, OK_Ordinary), + SubExpr(SubExpr), Kind(Kind), BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) + { + setDependence(computeDependence(this)); + } + + GetBoundExpr(Expr *SubExpr, BoundKind Kind, QualType ResultType) + : GetBoundExpr(SourceLocation(), SourceLocation(), SubExpr, Kind, + ResultType) + { } + + explicit GetBoundExpr(EmptyShell Empty) + : Expr(GetBoundExprClass, Empty), SubExpr(nullptr), Kind(BK_Lower) { + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == GetBoundExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getSubExpr()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getSubExpr()->getEndLoc() : RParenLoc; + } + + child_range children() { + return child_range(&SubExpr, &SubExpr + 1); + } + + const_child_range children() const { + return const_child_range(&SubExpr, &SubExpr + 1); + } + + Expr *getSubExpr() { return cast(SubExpr); } + const Expr *getSubExpr() const { return cast(SubExpr); } + void setSubExpr(Expr *NewValue) { SubExpr = NewValue; } + + BoundKind getBoundKind() const { return Kind; } + void setBoundKind(BoundKind K) { Kind = K; } +}; + +enum class BoundsCheckKind : unsigned { + FlexibleArrayCountAssign, + FlexibleArrayCountCast, + FlexibleArrayCountDeref +}; + +/// PredefinedBoundsCheckExpr - AST representation of bounds checks that +/// BoundsSafety added. This is a wrapper expression to wrap the expression +/// (GuardedExpr) that will be executed if the bounds check succeeds. The +/// subexpressions except the first one (GuardedExpr) are used for bounds check +/// whose semantics is determined by the kind of bounds check (BoundsCheckKind). +/// Most subexpressions are likely to be OpaqueValueExpr to avoid re-evaluating +/// expressions. As bounds checks are necessarily implicit, the expression uses +/// the source location of the wrapped expression. +class PredefinedBoundsCheckExpr final + : public Expr, + public llvm::TrailingObjects { + static constexpr unsigned CheckArgsOffset = 1; + + PredefinedBoundsCheckExpr(Expr *GuardedExpr, BoundsCheckKind Kind, + ArrayRef CheckArgs); + PredefinedBoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(PredefinedBoundsCheckExprClass, Empty) { + PredefinedBoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast(getTrailingObjects()); + } + + Stmt *const *getTrailingStmts() const { return getTrailingObjects(); } + + Expr **getSubExprs() { return reinterpret_cast(getTrailingStmts()); } + + Expr *const *getSubExprs() const { + return reinterpret_cast(getTrailingStmts()); + } + +public: + static PredefinedBoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs); + + static PredefinedBoundsCheckExpr *CreateEmpty(ASTContext &Ctx, + unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == PredefinedBoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { + return PredefinedBoundsCheckExprBits.NumChildren; + } + + unsigned getNumCheckArgs() const { + assert(getNumSubExprs() >= CheckArgsOffset); + return getNumSubExprs() - CheckArgsOffset; + } + + static constexpr unsigned getCheckArgsOffset() { return CheckArgsOffset; } + + typedef Expr *const *checkargs_iterator; + typedef const Expr *const *const_checkargs_iterator; + + checkargs_iterator checkargs_begin() { + return reinterpret_cast(getSubExprs() + + getCheckArgsOffset()); + } + const_checkargs_iterator checkargs_begin() const { + return reinterpret_cast(getSubExprs() + + getCheckArgsOffset()); + } + checkargs_iterator checkargs_end() { + return checkargs_begin() + getNumCheckArgs(); + } + const_checkargs_iterator checkargs_end() const { + return checkargs_begin() + getNumCheckArgs(); + } + + llvm::iterator_range checkargs() { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + llvm::iterator_range checkargs() const { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + BoundsCheckKind getKind() const { + return static_cast(PredefinedBoundsCheckExprBits.Kind); + } + + StringRef getKindName() const; + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + // This returns the pointer to the base struct with flexible array member. + const Expr *getFamBasePtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamPtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 1); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamCount() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 2); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +/// BoundsCheckExpr - AST representation of a bounds check that BoundsSafety added. +/// This wraps the expression that will be evaluated if the bounds check +/// succeeds or evaluated before the bounds checks if PostGuard is true. The object +/// also holds all the bounds to check. Many operands are +/// likely to be OpaqueValueExpr to avoid re-evaluating expressions. As bounds +/// checks are necessarily implicit, they never have a source location. +class BoundsCheckExpr final : + public Expr, public llvm::TrailingObjects { + BoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs); + BoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(BoundsCheckExprClass, Empty) { + BoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast(getTrailingObjects()); + } + + Stmt *const *getTrailingStmts() const { + return getTrailingObjects(); + } + + Expr **getSubExprs() { + return reinterpret_cast(getTrailingStmts()); + } + + Expr *const *getSubExprs() const { + return reinterpret_cast(getTrailingStmts()); + } + +public: + static BoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + Expr *Cond, ArrayRef CommonExprs); + + static BoundsCheckExpr *CreateEmpty(ASTContext &Ctx, unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { return BoundsCheckExprBits.NumChildren; } + unsigned getNumCommonExprs() const { + assert(getNumSubExprs() >= 2); + return getNumSubExprs() - 2; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast (getSubExprs() + 2); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast (getSubExprs() + 2); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumCommonExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumCommonExprs(); + } + + llvm::iterator_range opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getCond() { return getSubExpr(1); } + const Expr *getCond() const { return getSubExpr(1); } + void setCond(Expr *Cond) { setSubExpr(1, Cond); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +class MaterializeSequenceExpr final : + public Expr, public llvm::TrailingObjects { + + MaterializeSequenceExpr(Expr *WrappedExpr, ArrayRef Values, bool Unbind) + : Expr(MaterializeSequenceExprClass, WrappedExpr->getType(), + WrappedExpr->getValueKind(), + WrappedExpr->getObjectKind()) { + MaterializeSequenceExprBits.NumExprs = Values.size() + 1; + MaterializeSequenceExprBits.Unbind = Unbind; + getSubExprs()[0] = WrappedExpr; + std::copy(Values.begin(), Values.end(), getSubExprs() + 1); + setDependence(computeDependence(this)); + } + + MaterializeSequenceExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(MaterializeSequenceExprClass, Empty) { + MaterializeSequenceExprBits.NumExprs = NumExprs; + } + + Expr **getSubExprs() { + return getTrailingObjects(); + } + + Expr *const *getSubExprs() const { + return getTrailingObjects(); + } + + friend class ASTStmtReader; +public: + static MaterializeSequenceExpr *Create(const ASTContext &Ctx, Expr *WrappedExpr, + ArrayRef Values, + bool Unbind = false); + + static MaterializeSequenceExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == MaterializeSequenceExprClass; + } + + unsigned getNumSubExprs() const { + return MaterializeSequenceExprBits.NumExprs; + } + + bool isBinding() const { + return !isUnbinding(); + } + + bool isUnbinding() const { + return MaterializeSequenceExprBits.Unbind; + } + + unsigned getNumOpaqueValueExprs() const { + assert(getNumSubExprs() > 1); + return getNumSubExprs() - 1; + } + + Expr *getWrappedExpr() const { + assert(getNumSubExprs() > 0); + return getSubExprs()[0]; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast (getSubExprs() + 1); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast (getSubExprs() + 1); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + + llvm::iterator_range opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + SourceLocation getBeginLoc() const { + return getWrappedExpr()->getBeginLoc(); + } + + SourceLocation getEndLoc() const { + return getWrappedExpr()->getEndLoc(); + } + + child_range children() { + Stmt **begin = reinterpret_cast(getSubExprs()); + return child_range(begin, + begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast(getSubExprs()); + return const_child_range(begin, + begin + getNumSubExprs()); + } +}; + +/// TerminatedByToIndexableExpr - The AST representation of +/// __builtin_terminated_by_to_indexable() and +/// __builtin_unsafe_terminated_by_to_indexable(). +class TerminatedByToIndexableExpr final : public Expr { +private: + enum { POINTER, TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + bool IncludeTerminator; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByToIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *Terminator, bool IncludeTerminator, + QualType ResultType) + : Expr(TerminatedByToIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, Terminator}, IncludeTerminator(IncludeTerminator), + BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {} + + TerminatedByToIndexableExpr(Expr *Pointer, Expr *Terminator, + QualType ResultType) + : TerminatedByToIndexableExpr(SourceLocation(), SourceLocation(), Pointer, + Terminator, false, ResultType) {} + + explicit TerminatedByToIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByToIndexableExprClass, Empty), SubExprs{}, + IncludeTerminator(false) {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByToIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getPointer()->getEndLoc() : RParenLoc; + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getTerminator() const { return cast(SubExprs[TERMINATOR]); } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + bool includesTerminator() const { return IncludeTerminator; } + void setIncludeTerminator(bool Include) { IncludeTerminator = Include; } +}; + +/// TerminatedByFromIndexableExpr - The AST representation of +/// __builtin_unsafe_terminated_by_from_indexable(). +class TerminatedByFromIndexableExpr final : public Expr { +private: + enum { POINTER, POINTER_TO_TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByFromIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *PointerToTerminator, QualType ResultType) + : Expr(TerminatedByFromIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, PointerToTerminator}, BuiltinLoc(BuiltinLoc), + RParenLoc(RParenLoc) {} + + TerminatedByFromIndexableExpr(Expr *Pointer, Expr *PointerToTerminator, + QualType ResultType) + : TerminatedByFromIndexableExpr(SourceLocation(), SourceLocation(), + Pointer, PointerToTerminator, + ResultType) {} + + explicit TerminatedByFromIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByFromIndexableExprClass, Empty), SubExprs{} {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByFromIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + if (RParenLoc.isValid()) + return RParenLoc; + return getPointerToTerminator() ? getPointerToTerminator()->getEndLoc() + : SourceLocation(); + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getPointerToTerminator() const { + return cast_or_null(SubExprs[POINTER_TO_TERMINATOR]); + } + void setPointerToTerminator(Expr *E) { SubExprs[POINTER_TO_TERMINATOR] = E; } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// A builtin binary operation expression such as "x + y" or "x <= y". /// /// This expression node kind describes a builtin binary operation, @@ -5082,6 +5941,28 @@ class InitListExpr : public Expr { return cast_or_null(InitExprs[Init]); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + Expr *getInitForField(const ValueDecl *VD) { + assert(isSemanticForm()); + if (auto FD = dyn_cast(VD)) { + unsigned FieldIdx = 0; + for (FieldDecl *Sibling : FD->getParent()->fields()) { + if (Sibling->isUnnamedBitField()) + continue; + if (Sibling == FD) + break; + ++FieldIdx; + } + if (FieldIdx >= getNumInits()) + return nullptr; + return getInit(FieldIdx); + } + + auto IFD = cast(VD); + return getInitForField(IFD->getAnonField()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void setInit(unsigned Init, Expr *expr) { assert(Init < getNumInits() && "Initializer access out of range!"); InitExprs[Init] = expr; diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index 917bada61fa6f..e7a2c49fde863 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -45,6 +45,35 @@ const Expr *IgnoreExprNodes(const Expr *E, FnTys &&...Fns) { return IgnoreExprNodes(const_cast(E), std::forward(Fns)...); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline Expr *IgnoreBoundsSafetyImplicitImpl( + Expr *E, llvm::SmallPtrSetImpl &BoundValues) { + + if (auto *FPPE = dyn_cast(E)) + return FPPE->getSubExpr(); + + if (auto *AE = dyn_cast(E)) + return AE->getWrappedExpr(); + + if (auto *MSE = dyn_cast(E)) { + if (!MSE->isUnbinding()) + BoundValues.insert(MSE->opaquevalues_begin(), MSE->opaquevalues_end()); + return MSE->getWrappedExpr(); + } + + if (auto *BCE = dyn_cast(E)) { + BoundValues.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + return BCE->getGuardedExpr(); + } + + if (auto *Opaque = dyn_cast(E)) + if (BoundValues.count(Opaque)) + return Opaque->getSourceExpr(); + + return E; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline Expr *IgnoreImplicitCastsSingleStep(Expr *E) { if (auto *ICE = dyn_cast(E)) return ICE->getSubExpr(); diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 8788b8ff0ef0a..c9d631b09cdda 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion) // Convert an integer initializer to an OpenCL sampler. CAST_OPERATION(IntToOCLSampler) +// BoundsSafety: Casting between pointer types with different -fbounds-safety attributes +CAST_OPERATION(BoundsSafetyPointerCast) + // Truncate a vector type by dropping elements from the end (HLSL only). CAST_OPERATION(HLSLVectorTruncation) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 7f0029b040995..18bb674351737 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -78,7 +78,17 @@ struct PrintingPolicy { PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true), UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), - UseEnumerators(true), UseHLSLTypes(LO.HLSL) {} + UseEnumerators(true), UseHLSLTypes(LO.HLSL), + /* TO_UPSTREAM(BoundsSafety) ON */ + CountedByInArrayBracket(false), DelayedArrayQual() { + // GNU Attributes cannot be placed inside the array bracket, hence 'counted_by' + // attribute in the upstream is printed after the array bracket. Internally, + // 'counted_by' can be either inside or outside the bracket, while the inside + // has been the primary style. For now, we print it inside the bracket when + // -fbounds-safety is enabled, and outside the bracket otherwise. + CountedByInArrayBracket = LO.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -355,8 +365,14 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned UseHLSLTypes : 1; + /// Whether to print 'counted_by' attribute inside the array bracket. + LLVM_PREFERRED_TYPE(bool) + unsigned CountedByInArrayBracket : 1; + /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; + + std::string DelayedArrayQual; }; } // end namespace clang diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 1c4077531cbde..38ead93da8bf3 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -135,6 +135,7 @@ def QualType : DefaultValuePropertyType; def RefQualifierKind : EnumPropertyType; def Selector : PropertyType; def SourceLocation : PropertyType; +def BoundsSafetyPointerAttributes : PropertyType; def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } def ExprRef : SubclassPropertyType<"Expr", StmtRef>; def TemplateArgument : PropertyType; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index e3c0cb46799f7..2f1786bf7dd10 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1143,6 +1143,16 @@ DEF_TRAVERSE_TYPE(CountAttributedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPE(DynamicRangePointerType, { + if (auto *EndPtr = T->getEndPointer()) + TRY_TO(TraverseStmt(EndPtr)); + TRY_TO(TraverseType(T->desugar())); +}) + +DEF_TRAVERSE_TYPE(ValueTerminatedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPE(BTFTagAttributedType, { TRY_TO(TraverseType(T->getWrappedType())); }) @@ -1442,6 +1452,14 @@ DEF_TRAVERSE_TYPELOC(AttributedType, DEF_TRAVERSE_TYPELOC(CountAttributedType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPELOC(DynamicRangePointerType, + { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) + +DEF_TRAVERSE_TYPELOC(ValueTerminatedType, + { TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPELOC(BTFTagAttributedType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) @@ -2537,6 +2555,24 @@ DEF_TRAVERSE_STMT(BuiltinBitCastExpr, { TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(BoundsSafetyPointerPromotionExpr, {}) +DEF_TRAVERSE_STMT(AssumptionExpr, {}) +DEF_TRAVERSE_STMT(ForgePtrExpr, {}) +DEF_TRAVERSE_STMT(GetBoundExpr, {}) +DEF_TRAVERSE_STMT(PredefinedBoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(BoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(MaterializeSequenceExpr, { + if (!S->isUnbinding()) { + for (auto *OVE : S->opaquevalues()) { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(OVE->getSourceExpr()); + } + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getWrappedExpr()); + ShouldVisitChildren = false; + } +}) +DEF_TRAVERSE_STMT(TerminatedByToIndexableExpr, {}) +DEF_TRAVERSE_STMT(TerminatedByFromIndexableExpr, {}) + template bool RecursiveASTVisitor::TraverseSynOrSemInitListExpr( InitListExpr *S, DataRecursionQueue *Queue) { diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index bbd7634bcc3bf..a32b84c8de0b0 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -724,6 +724,55 @@ class alignas(void *) Stmt { unsigned TemplateDepth; }; + class AssumptionExprBitfields { + friend class ASTStmtReader; + friend class AssumptionExpr; + + unsigned : NumExprBits; + unsigned NumExprs : 4; + }; + + class PredefinedBoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class PredefinedBoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned Kind : 6; + unsigned NumChildren; + }; + + class BoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class BoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned NumChildren : 31; + }; + + class BoundsSafetyPointerPromotionExprBitfields { + friend class ASTStmtReader; + friend class BoundsSafetyPointerPromotionExpr; + + unsigned : NumExprBits; + + /// If this field is is set, CodeGen should perform a null check on the + /// pointer and skip evaluating the bounds to avoid introducing unexpected + /// null dereference. + unsigned NullCheck : 1; + }; + + class MaterializeSequenceExprBitfields { + friend class ASTStmtReader; + friend class MaterializeSequenceExpr; + + unsigned : NumExprBits; + + unsigned NumExprs : 32 - 1; + unsigned Unbind : 1; + }; + //===--- C++ Expression bitfields classes ---===// class CXXOperatorCallExprBitfields { @@ -1237,6 +1286,13 @@ class alignas(void *) Stmt { // GNU Extensions. StmtExprBitfields StmtExprBits; + // BoundsSafety Expression + AssumptionExprBitfields AssumptionExprBits; + PredefinedBoundsCheckExprBitfields PredefinedBoundsCheckExprBits; + BoundsCheckExprBitfields BoundsCheckExprBits; + BoundsSafetyPointerPromotionExprBitfields BoundsSafetyPointerPromotionExprBits; + MaterializeSequenceExprBitfields MaterializeSequenceExprBits; + // C++ Expressions CXXOperatorCallExprBitfields CXXOperatorCallExprBits; CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 39dd1f515c9eb..0582fd2c67612 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -318,6 +318,10 @@ class TextNodeDumper void VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr *Node); void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node); void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node); + void VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *Node); + void VisitBoundsCheckExpr(const BoundsCheckExpr *Node); + void VisitPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *Node); + void VisitGetBoundExpr(const GetBoundExpr *Node); void VisitOMPIteratorExpr(const OMPIteratorExpr *Node); void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node); void VisitRequiresExpr(const RequiresExpr *Node); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 29ddef540035e..971cbddd8ed00 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2265,6 +2265,17 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { }; static_assert(sizeof(CountAttributedTypeBitfields) <= sizeof(unsigned)); + /* TO_UPSTREAM(BoundsSafety) ON */ + class DynamicRangePointerTypeBitfields { + friend class DynamicRangePointerType; + + unsigned : NumTypeBits; + + unsigned NumEndPtrDecls : 16; + unsigned NumStartPtrDecls : 16; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + union { TypeBitfields TypeBits; ArrayTypeBitfields ArrayTypeBits; @@ -2288,6 +2299,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { DependentTemplateSpecializationTypeBits; PackExpansionTypeBitfields PackExpansionTypeBits; CountAttributedTypeBitfields CountAttributedTypeBits; + /* TO_UPSTREAM(BoundsSafety) ON */ + DynamicRangePointerTypeBitfields DynamicRangePointerTypeBits; + /* TO_UPSTREAM(BoundsSafety) OFF */ }; private: @@ -2435,12 +2449,48 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// class), will be set to the declaration. bool isIncompleteType(NamedDecl **Def = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if the size of this type is "meaningless". For a meaninglessly + /// sized type T, sizeof(T) is either ill-formed or has a useless or + /// misleading result. This could be because the type is a function type, is a + /// void type, is a sizeless type, is an incomplete type (including an + /// incomplete array type), or is a record type with a flexible array member. + bool isSizeMeaningless() const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return true if this is an incomplete or object /// type, in other words, not a function type. bool isIncompleteOrObjectType() const { return !isFunctionType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if this is an incomplete or sizeless type including a function type. + bool isIncompleteOrSizelessType() const { + return isIncompleteType() || isSizelessType() || isFunctionType(); + } + + /// \returns True if the type is incomplete and it is also a type that + /// cannot be completed by a later type definition. + /// + /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false + /// because a definition for `ForwardDecl` could be provided later on in the + /// translation unit. + /// + /// Note even for types that this function returns true for it is still + /// possible for the declarations that contain this type to later have a + /// complete type in a translation unit. E.g.: + /// + /// \code{.c} + /// // This decl has type 'char[]' which is incomplete and cannot be later + /// // completed by another by another type declaration. + /// extern char foo[]; + /// // This decl how has complete type 'char[5]'. + /// char foo[5]; // foo has a complete type + /// \endcode + bool isIncompletableIncompleteType() const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Determine whether this type is an object type. bool isObjectType() const { // C++ [basic.types]p8: @@ -2534,8 +2584,23 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionProtoType() const { return getAs(); } bool isPointerType() const; bool isSignableType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnsafeIndexablePointerType() const; + bool isSinglePointerType() const; + bool isIndexablePointerType() const; + bool isBidiIndexablePointerType() const; + bool isUnspecifiedPointerType() const; + bool isSafePointerType() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isAnyVaListType(ASTContext &) const; + bool isDynamicRangePointerType() const; + bool isBoundsAttributedType() const; + bool isValueTerminatedType() const; + bool isImplicitlyNullTerminatedType(const ASTContext &) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isBlockPointerType() const; bool isVoidPointerType() const; bool isReferenceType() const; @@ -2628,6 +2693,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isAtomicType() const; // C11 _Atomic() bool isUndeducedAutoType() const; // C++11 auto or // C++14 decltype(auto) + // TO_UPSTREAM(BoundsSafety) + bool isPointerTypeWithBounds() const; // BoundsSafety __indexable or __bidi_indexable bool isTypedefNameType() const; // typedef or alias template #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ @@ -3007,6 +3074,16 @@ template <> const BoundsAttributedType *Type::getAs() const; /// sugar until it reaches an CountAttributedType or a non-sugared type. template <> const CountAttributedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// This will check for a DynamicRangePointerType by removing any existing +/// sugar until it reaches an DynamicRangePointerType or a non-sugared type. +template <> const DynamicRangePointerType *Type::getAs() const; + +/// This will check for a ValueTerminatedType by removing any existing sugar +/// until it reaches a ValueTerminatedType or a non-sugared type. +template <> const ValueTerminatedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) OFF */ + // We can do canonical leaf types faster, because we don't have to // worry about preserving child type decoration. #define TYPE(Class, Base) @@ -3181,28 +3258,253 @@ class ParenType : public Type, public llvm::FoldingSetNode { static bool classof(const Type *T) { return T->getTypeClass() == Paren; } }; +class BoundsSafetyPointerAttributes { +public: + // This ordering of attributes also serves as precedence to create a composite + // type between two pointer types. We currently use the precedence for conditional + // operators for which '__unsafe_indexable' has the highest precedence while + // '__single' being the lowest. + enum BK { + Unspecified = 0, + UnsafeIndexable = 1, + BidiIndexable = 2, + Indexable = 3, + Single = 4, + BoundsWidth = 3, + BoundsMask = (1 << BoundsWidth) - 1 + }; + + enum TK { + UnsafeCastable = 1, + NoCastable = 2, + Castable = 3, + TypeWidth = 2, + TypeMask = (1 << TypeWidth) - 1, + ShiftedTypeMask = TypeMask << BoundsWidth + }; + + static BoundsSafetyPointerAttributes unsafeIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setUnsafeIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes bidiIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setBidiIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes indexable() { + BoundsSafetyPointerAttributes FA; + FA.setIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes single() { + BoundsSafetyPointerAttributes FA; + FA.setSingle(); + return FA; + } + + static BoundsSafetyPointerAttributes unspecified() { + BoundsSafetyPointerAttributes FA; + return FA; + } + + uint32_t getBoundsAttr() const { return BoundsAttr; } + + uint32_t getTypeAttr() const { return TypeAttr; } + + uint32_t getAsOpaqueValue() const { + return (TypeAttr << BoundsWidth) | BoundsAttr; + } + + bool isUnspecified() const { return getBoundsAttr() == Unspecified; } + bool isUnsafeIndexable() const { return getBoundsAttr() == UnsafeIndexable; } + bool isSingle() const { return getBoundsAttr() == Single; } + bool isIndexable() const { return getBoundsAttr() == Indexable; } + bool isBidiIndexable() const { return getBoundsAttr() == BidiIndexable; } + bool isUnsafeOrUnspecified() const { return isUnsafeIndexable() || isUnspecified(); } + + void setUnspecified() { setBoundsAttr(Unspecified); } + void setUnsafeIndexable() { setBoundsAttr(UnsafeIndexable); } + void setSingle() { setBoundsAttr(Single); } + void setIndexable() { setBoundsAttr(Indexable); } + void setBidiIndexable() { setBoundsAttr(BidiIndexable); } + void copyBoundsAttr(BoundsSafetyPointerAttributes FAttr) { + setBoundsAttr(FAttr.getBoundsAttr()); + } + + void copyTypeAttr(BoundsSafetyPointerAttributes FAttr) { + setTypeAttr(FAttr.getTypeAttr()); + } + + BoundsSafetyPointerAttributes(uint32_t value = 0) + : BoundsAttr(value & BoundsMask), + TypeAttr((value & ShiftedTypeMask) >> BoundsWidth) {} + + void print(raw_ostream &OS) const { + switch (getBoundsAttr()) { + case Unspecified: + break; + case UnsafeIndexable: + OS << "__unsafe_indexable"; + break; + case Single: + OS << "__single"; + break; + case Indexable: + OS << "__indexable"; + break; + case BidiIndexable: + OS << "__bidi_indexable"; + break; + default: + llvm_unreachable("Unknown -fbounds-safety bounds only attributes"); + } + + switch (getTypeAttr()) { + case Unspecified: + break; + default: + llvm_unreachable("Unknown -fbounds-safety type attributes"); + } + } + + bool operator==(BoundsSafetyPointerAttributes other) const { + return getAsOpaqueValue() == other.getAsOpaqueValue(); + } + + bool operator!=(BoundsSafetyPointerAttributes other) const { + return !(*this == other); + } + + bool hasRawPointerLayout() const { + return isUnspecified() || isSingle() || isUnsafeIndexable(); + } + + bool hasLowerBound() const { return isBidiIndexable(); } + + bool hasUpperBound() const { return isBidiIndexable() || isIndexable(); } + + void takeBoundsAttr(BoundsSafetyPointerAttributes Other) { + setBoundsAttr(Other.getBoundsAttr()); + } + + static bool areEquivalentLayouts(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + if (A1 == A2) + return true; + + if (A1.getTypeAttr() != A2.getTypeAttr()) + return false; + + // Single and unsafe have equivalent layout. + if ((A1.isUnsafeOrUnspecified() && A2.hasRawPointerLayout()) || + (A1.hasRawPointerLayout() && A2.isUnsafeOrUnspecified())) + return true; + + return false; + } + + + static bool areCompatible(BoundsSafetyPointerAttributes Left, + BoundsSafetyPointerAttributes Right) { + if (Left == Right) + return true; + + // Allow unspecified pointer attributes to merge with either of + // unsafe_indexable and single. + if (Left.isUnspecified()) + return Right.hasRawPointerLayout(); + if (Right.isUnspecified()) + return Left.hasRawPointerLayout(); + + return false; + } + + static BoundsSafetyPointerAttributes fromOpaqueValue(uint32_t value) { + return BoundsSafetyPointerAttributes(value); + } + + // BoundsSafetyPointerAttributes precedence in conditional operator + // conversion (bounds only): + // 1) __unsafe_indexable or unspecified + // 2) __bidi_indexable + // 3) __indexable + // 4) __single + static BoundsSafetyPointerAttributes merge(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + uint32_t NewBoundsAtt = A1.getBoundsAttr() > A2.getBoundsAttr() + ? A2.getBoundsAttr() + : A1.getBoundsAttr(); + + uint32_t NewTypeAtt = A1.getTypeAttr() > A2.getTypeAttr() + ? A2.getTypeAttr() + : A1.getTypeAttr(); + + BoundsSafetyPointerAttributes NewAtt; + NewAtt.setBoundsAttr(NewBoundsAtt); + NewAtt.setTypeAttr(NewTypeAtt); + return NewAtt; + } + +private: + void setBoundsAttr(uint32_t attr) { + assert(!(attr & ~BoundsMask) && "attr contains non-bounds bits"); + BoundsAttr = attr; + } + void setTypeAttr(uint32_t attr) { + assert(!(attr & ~TypeMask) && "attr contains non-type bits"); + TypeAttr = attr; + } + +private: + // |0 .. 2|3 .. 4|5 .. 31| + // |Bounds|Type |Reserved| + uint32_t BoundsAttr : 3; + uint32_t TypeAttr : 2; +}; + /// PointerType - C99 6.7.5.1 - Pointer Declarators. class PointerType : public Type, public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these. + using FA = BoundsSafetyPointerAttributes; + QualType PointeeType; + FA FAttr; - PointerType(QualType Pointee, QualType CanonicalPtr) + PointerType(QualType Pointee, QualType CanonicalPtr, FA FAttr) : Type(Pointer, CanonicalPtr, Pointee->getDependence()), - PointeeType(Pointee) {} + PointeeType(Pointee), FAttr(FAttr) {} public: QualType getPointeeType() const { return PointeeType; } + FA getPointerAttributes() const { return FAttr; } bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } + bool hasRawPointerLayout() const { return FAttr.hasRawPointerLayout(); } + bool isSafePointer() const { + return !(FAttr.isUnsafeIndexable() || FAttr.isUnspecified()); + } + + bool isSingle() const { return FAttr.isSingle(); } + bool isIndexable() const { return FAttr.isIndexable(); } + bool isBidiIndexable() const { return FAttr.isBidiIndexable(); } + bool isUnsafeIndexable() const { return FAttr.isUnsafeIndexable(); } + bool isUnspecified() const { return FAttr.isUnspecified(); } + void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getPointeeType()); + Profile(ID, getPointeeType(), getPointerAttributes()); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee) { + static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee, FA FAttr) { ID.AddPointer(Pointee.getAsOpaquePtr()); + ID.AddInteger(FAttr.getAsOpaqueValue()); } static bool classof(const Type *T) { return T->getTypeClass() == Pointer; } @@ -3212,12 +3514,16 @@ class PointerType : public Type, public llvm::FoldingSetNode { /// arguments of the `counted_by` attribute and the likes. class TypeCoupledDeclRefInfo { public: - using BaseTy = llvm::PointerIntPair; + using BaseTy = llvm::PointerIntPair; private: enum { DerefShift = 0, DerefMask = 1, + /*TO_UPSTREAM(BoundsSafety) ON*/ + MemberShift = 1, + MemberMask = 1, + /*TO_UPSTREAM(BoundsSafety) OFF*/ }; BaseTy Data; @@ -3225,9 +3531,17 @@ class TypeCoupledDeclRefInfo { /// \p D is to a declaration referenced by the argument of attribute. \p Deref /// indicates whether \p D is referenced as a dereferenced form, e.g., \p /// Deref is true for `*n` in `int *__counted_by(*n)`. - TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// \p Member indicates that \p D is referenced as the member for a struct, + /// e.g __counted_by(hdr.len) `hdr`, although a FieldDecl is referred to by + /// a DeclRefExpr, while `len` is referred to by a MemberExpr. In this + /// example `Member` is false for `hdr` and true for `len`. + TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false, + bool Member = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isDeref() const; + bool isMember() const; ValueDecl *getDecl() const; unsigned getInt() const; void *getOpaqueValue() const; @@ -3251,6 +3565,16 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon); public: + /* TO_UPSTREAM(BoundsSafety) ON*/ + enum BoundsAttrKind { + CountedBy = 0, + SizedBy, + CountedByOrNull, + SizedByOrNull, + EndedBy + }; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isSugared() const { return true; } QualType desugar() const { return WrappedTy; } @@ -3278,6 +3602,9 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { // annotations. switch (T->getTypeClass()) { case CountAttributed: +/* TO_UPSTREAM(BoundsSafety) ON */ + case DynamicRangePointer: +/* TO_UPSTREAM(BoundsSafety) OFF */ return true; default: return false; @@ -3309,22 +3636,17 @@ class CountAttributedType final } public: - enum DynamicCountPointerKind { - CountedBy = 0, - SizedBy, - CountedByOrNull, - SizedByOrNull, - }; - Expr *getCountExpr() const { return CountExpr; } bool isCountInBytes() const { return CountAttributedTypeBits.CountInBytes; } bool isOrNull() const { return CountAttributedTypeBits.OrNull; } - DynamicCountPointerKind getKind() const { + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsAttrKind getKind() const { if (isOrNull()) return isCountInBytes() ? SizedByOrNull : CountedByOrNull; return isCountInBytes() ? SizedBy : CountedBy; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, desugar(), CountExpr, isCountInBytes(), isOrNull()); @@ -3336,8 +3658,125 @@ class CountAttributedType final static bool classof(const Type *T) { return T->getTypeClass() == CountAttributed; } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + StringRef GetAttributeName(bool WithMacroPrefix) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerType final + : public BoundsAttributedType, + public llvm::TrailingObjects { + friend class ASTContext; + + // We have 0 or 1 primary start and end pointer expressions and 0 or more + // start and end declarations that are associated with the type. + Expr *StartPtr; + Expr *EndPtr; + DynamicRangePointerType(QualType PointerTy, QualType CanPointerTy, + Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls); + + unsigned numTrailingObjects(OverloadToken) const { + return DynamicRangePointerTypeBits.NumEndPtrDecls + + DynamicRangePointerTypeBits.NumStartPtrDecls; + } + +public: + BoundsAttrKind getKind() const { return EndedBy; } + + Expr *getStartPointer() const { return StartPtr; } + Expr *getEndPointer() const { return EndPtr; } + + unsigned getNumStartPtrDecls() const { + return DynamicRangePointerTypeBits.NumStartPtrDecls; + } + + unsigned getNumEndPtrDecls() const { + return DynamicRangePointerTypeBits.NumEndPtrDecls; + } + + decl_iterator startptr_decl_begin() const { + return getTrailingObjects(); + } + + decl_iterator startptr_decl_end() const { + return startptr_decl_begin() + getNumStartPtrDecls(); + } + + decl_range startptr_decls() const { + return decl_range(startptr_decl_begin(), startptr_decl_end()); + } + + ArrayRef getStartPtrDecls() const { + return {startptr_decl_begin(), startptr_decl_end()}; + } + + decl_iterator endptr_decl_begin() const { + return startptr_decl_end(); + } + + decl_iterator endptr_decl_end() const { + return endptr_decl_begin() + getNumEndPtrDecls(); + } + + decl_range endptr_decls() const { + return decl_range(endptr_decl_begin(), endptr_decl_end()); + } + + ArrayRef getEndPtrDecls() const { + return {endptr_decl_begin(), endptr_decl_end()}; + } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, desugar(), StartPtr, EndPtr, getNumStartPtrDecls(), + getNumEndPtrDecls()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType PointerTy, + Expr *StartPtr, Expr *EndPtr, unsigned NumStartDecls, + unsigned NumEndDecls); + + static bool classof(const Type *T) { + return T->getTypeClass() == DynamicRangePointer; + } +}; + +class ValueTerminatedType final : public Type, public llvm::FoldingSetNode { + friend class ASTContext; + + QualType OriginalTy; + Expr *TerminatorExpr; + + explicit ValueTerminatedType(QualType OriginalTy, QualType CanOriginalTy, + Expr *TerminatorExpr) + : Type(ValueTerminated, CanOriginalTy, OriginalTy->getDependence()), + OriginalTy(OriginalTy), TerminatorExpr(TerminatorExpr) {} + +public: + bool isSugared() const { return true; } + QualType desugar() const { return OriginalTy; } + + Expr *getTerminatorExpr() const { return TerminatorExpr; } + + llvm::APSInt getTerminatorValue(const ASTContext &Ctx) const; + + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) const { + Profile(ID, Ctx, OriginalTy, TerminatorExpr); + } + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx, + QualType OriginalTy, const Expr *TerminatorExpr); + + static bool classof(const Type *T) { + return T->getTypeClass() == ValueTerminated; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Represents a type which was implicitly adjusted by the semantic /// engine for arbitrary reasons. For example, array and function types can /// decay, and function types can have their calling conventions adjusted. @@ -8110,6 +8549,59 @@ inline bool Type::isAnyPointerType() const { inline bool Type::isSignableType() const { return isPointerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline bool Type::isUnsafeIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + if (!PT) + return false; + if (PT->isUnsafeIndexable()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrUnsafeIndexable)) + return true; + return false; +} + +inline bool Type::isSinglePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + if (!PT) + return false; + if (PT->isSingle()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrSingle)) + return true; + return false; +} + +inline bool Type::isBidiIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->isBidiIndexable(); +} + +inline bool Type::isIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->isIndexable(); +} + +inline bool Type::isUnspecifiedPointerType() const { + return isPointerType() && + !(isUnsafeIndexablePointerType() || isSinglePointerType() || + isBidiIndexablePointerType() || isIndexablePointerType() || + isBoundsAttributedType() || isValueTerminatedType()); +} + +inline bool Type::isSafePointerType() const { + return isPointerType() && + (isSinglePointerType() || isBidiIndexablePointerType() || + isIndexablePointerType() || isBoundsAttributedType() || + isValueTerminatedType()); +} + +inline bool Type::isPointerTypeWithBounds() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->getPointerAttributes().hasUpperBound(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline bool Type::isBlockPointerType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 9f2dff7a782cb..0acf15245b684 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1146,6 +1146,40 @@ class CountAttributedTypeLoc final SourceRange getLocalSourceRange() const; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerTypeLoc final + : public InheritingConcreteTypeLoc { +public: + Expr *getStartPointer() const { return getTypePtr()->getStartPointer(); } + Expr *getEndPointer() const { return getTypePtr()->getEndPointer(); } + + SourceRange getLocalSourceRange() const; +}; + +struct ValueTerminatedTypeLocInfo {}; + +class ValueTerminatedTypeLoc final + : public ConcreteTypeLoc { +public: + TypeLoc getOriginalLoc() const { return getInnerTypeLoc(); } + + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + // do nothing + } + + QualType getInnerType() const { return getTypePtr()->desugar(); } + + SourceRange getLocalSourceRange() const { return {}; } + + unsigned getLocalDataSize() const { return 0; } + + Expr *getTerminatorExpr() const { return getTypePtr()->getTerminatorExpr(); } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct MacroQualifiedLocInfo { SourceLocation ExpansionLoc; }; @@ -1307,6 +1341,10 @@ class PointerTypeLoc : public PointerLikeTypeLocgetTypePtr()->getPointerAttributes(); + } }; /// Wrapper for source info for block pointers. diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 1e4a0d52e4d42..52b110fff244c 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -22,7 +22,13 @@ let Class = PointerType in { let Read = [{ node->getPointeeType() }]; } - def : Creator<[{ return ctx.getPointerType(pointeeType); }]>; + // TO_UPSTREAM(BoundsSafety) ON + def : Property<"pointerAttributes", BoundsSafetyPointerAttributes> { + let Read = [{ node->getPointerAttributes() }]; + } + + def : Creator<[{ return ctx.getPointerType(pointeeType, pointerAttributes); }]>; + // TO_UPSTREAM(BoundsSafety) OFF } let Class = CountAttributedType in { @@ -44,6 +50,40 @@ let Class = CountAttributedType in { def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>; } +// TO_UPSTREAM(BoundsSafety) ON +let Class = DynamicRangePointerType in { + def : Property<"PointerTy", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"StartPtr", ExprRef> { + let Read = [{ node->getStartPointer() }]; + } + def : Property<"EndPtr", ExprRef> { + let Read = [{ node->getEndPointer() }]; + } + def : Property<"StartPtrDecls", Array> { + let Read = [{ node->getStartPtrDecls() }]; + } + def : Property<"EndPtrDecls", Array> { + let Read = [{ node->getEndPtrDecls() }]; + } + def : Creator<[{ return ctx.getDynamicRangePointerType(PointerTy, StartPtr, EndPtr, + StartPtrDecls, EndPtrDecls); }]>; +} + +let Class = ValueTerminatedType in { + def : Property<"originalType", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"terminatorExpr", ExprRef> { + let Read = [{ node->getTerminatorExpr() }]; + } + def : Creator<[{ + return ctx.getValueTerminatedType(originalType, terminatorExpr); + }]>; +} +// TO_UPSTREAM(BoundsSafety) OFF + let Class = AdjustedType in { def : Property<"originalType", QualType> { let Read = [{ node->getOriginalType() }]; diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index 267cde64f8f23..403611ae7b07c 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -126,6 +126,16 @@ class UnsafeBufferUsageHandler { bool IsRelatedToDecl, ASTContext &Ctx) = 0; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Invoked when an unsafe passing to count-attributed pointer is found. + virtual void handleUnsafeCountAttributedPointerArgument(const CallExpr *Call, + const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) { + handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Invoked when a fix is suggested against a variable. This function groups /// all variables that must be fixed together (i.e their types must be changed /// to the same target type to prevent type mismatches) into a single fixit. diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def index 09fa510bc0472..3db9d8b4962c0 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -38,6 +38,8 @@ WARNING_GADGET(PointerArithmetic) WARNING_GADGET(UnsafeBufferUsageAttr) WARNING_GADGET(UnsafeBufferUsageCtorAttr) WARNING_GADGET(DataInvocation) +// TO_UPSTREAM(BoundsSafety) +WARNING_GADGET(CountAttributedPointerArgument) WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall) WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 72c7174d9974e..47f322eac1461 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -257,6 +257,8 @@ class VariadicUnsignedArgument : Argument; class VariadicExprArgument : Argument; class VariadicStringArgument : Argument; class VariadicIdentifierArgument : Argument; +// TO_UPSTREAM(BoundsSafety) +class VariadicDeclArgument : Argument; // Like VariadicUnsignedArgument except values are ParamIdx. class VariadicParamIdxArgument : Argument; @@ -427,6 +429,19 @@ def HLSL : LangOpt<"HLSL">; // Language option for CMSE extensions def Cmse : LangOpt<"Cmse">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafety : LangOpt<"BoundsSafety">; +def BoundsSafetyAttributes : LangOpt<"BoundsSafetyAttributes">; + +// 'counted_by' is now available without -fbounds-safety for COnly (!LangOpts.CPlusPlus). +// Sema still ignores the attribute (with a warning) if it's applied to subjects other than +// flexible array members. +def CountedBySupported : LangOpt<"", [{ + // LangOpts.BoundsSafetyAttributes is to make it still available for experimental C++/Obj-C++. + !LangOpts.CPlusPlus || LangOpts.BoundsSafetyAttributes +}]>; +// TO_UPSTREAM(BoundsSafety) OFF + // Defines targets for target-specific attributes. Empty lists are unchecked. class TargetSpec { // Specifies Architectures for which the target applies, based off the @@ -2327,43 +2342,149 @@ def TypeNullUnspecified : TypeAttr { def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def CountedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"counted_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedBy : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF +} + +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety pointer type attributes. +def PtrUnsafeIndexable : TypeAttr { + let Spellings = [Clang<"unsafe_indexable">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrSingle : TypeAttr { + let Spellings = [Clang<"single">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrIndexable : TypeAttr { + let Spellings = [Clang<"indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrBidiIndexable : TypeAttr { + let Spellings = [Clang<"bidi_indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrEndedBy : DeclOrTypeAttr { + let Spellings = [Clang<"ended_by">]; + let Args = [ExprArgument<"EndPtr">, IntArgument<"NestedLevel">]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrTerminatedBy : TypeAttr { + let Spellings = [Clang<"terminated_by">]; + let Args = [ExprArgument<"Terminator">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def UnsafeLateConst : InheritableAttr { + let Spellings = [Clang<"unsafe_late_const">]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def ArrayDecayDiscardsCountInParameters : TypeAttr { + let Spellings = [Clang<"decay_discards_count_in_parameters">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +// BoundsSafety: Don't need to gate implicit attributes. +def PtrAutoAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def PtrAutoNullTerminatedAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def DependerDecls : InheritableAttr { + let Spellings = []; + let Args = [BoolArgument<"IsDeref">, + VariadicDeclArgument<"DependerDecls">, + VariadicUnsignedArgument<"DependerLevels">]; + let Subjects = SubjectList<[Field, Var]>; + let Documentation = [InternalOnly]; } +// TO_UPSTREAM(BoundsSafety) OFF // This is a marker used to indicate that an __unsafe_unretained qualifier was // ignored because ARC is not enabled. The usual representation for this diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index c1cb754287317..9639db4bc5502 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -321,6 +321,15 @@ CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. CODEGENOPT(VectorizeSLP , 1, 0) ///< Run SLP vectorizer. CODEGENOPT(ProfileSampleAccurate, 1, 0) ///< Sample profile is accurate. +/* TO_UPSTREAM(BoundsSafety) ON*/ +CODEGENOPT(TrapFuncReturns , 1, 0) ///< When true, the function specified with + ///< -ftrap-function may return normally +CODEGENOPT( + UniqueTrapBlocks, 1, + 0) ///< When true, basic blocks that contain traps they will be prevented + ///< from being merged by optimization passes and the backends. +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Treat loops as finite: language, always, never. ENUM_CODEGENOPT(FiniteLoops, FiniteLoopsKind, 2, FiniteLoopsKind::Language) diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index a024f9b2a9f8c..87d35096beb74 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -1030,6 +1030,11 @@ def warn_unpacked_field InGroup, DefaultIgnore; +// TO_UPSTREAM(BoundsSafety) ON +def err_bounds_safety_evaluate_no_bounds : Error< + "cannot get %select{lower,upper}0 bound because object size is unknown">; +// TO_UPSTREAM(BoundsSafety) OFF + // -Wunaligned-access def warn_unaligned_access : Warning< "field %1 within %0 is less aligned than %2 and is usually due to %0 being " diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 571f6146a63ae..f0faa876a168c 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -265,6 +265,10 @@ def err_drv_cannot_read_config_file : Error< "cannot read configuration file '%0': %1">; def err_drv_arg_requires_bitcode_input: Error< "option '%0' requires input to be LLVM bitcode">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_option_requires_option: Error< + "cannot use '%0' without using '%1'">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_target_unsupported_arch : Error<"the target architecture '%0' is not supported by the target '%1'">; @@ -300,6 +304,10 @@ def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">; def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">; def err_drv_invalid_value_with_suggestion : Error< "invalid value '%1' in '%0', expected one of: %2">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_invalid_value_with_flag_suggestion : Error< + "invalid value '%1' in '%0'; did you mean '%2'?">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_drv_alignment_not_power_of_two : Error<"alignment is not a power of 2 in '%0'">; def err_drv_invalid_remap_file : Error< "invalid option '%0' not of the form ;">; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index c5178bc8049a6..0cf7bfffab0ed 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -363,6 +363,38 @@ def warn_alias_with_section : Warning< "as the %select{aliasee|resolver}2">, InGroup; +// TO_UPSTREAM(BoundsSafety) ON +def error_bounds_safety_lang_not_supported : Error< + "-fbounds-safety is supported only for C language">; + +def err_bounds_safety_attributes_cannot_be_disabled : Error< + "-fexperimental-bounds-safety-attributes cannot be disabled when " + "-fbounds-safety is enabled">; + +def err_bounds_safety_initializer_out_of_range : Error< + "initializing value is out of valid range">; + +def warn_bounds_attributes_cxx_experimental_ignored : Warning< + "-fbounds-attributes-cxx-experimental without -fbounds-attributes is " + "ignored">, + InGroup; + +def warn_bounds_attributes_objc_experimental_ignored : Warning< + "-fbounds-attributes-objc-experimental without -fbounds-attributes is " + "ignored">, + InGroup; + +def warn_bounds_safety_relaxed_system_headers_ignored : Warning< + "-fno-bounds-safety-relaxed-system-headers without -fbounds-safety is " + "ignored">, + InGroup; + +def warn_bounds_safety_adoption_mode_ignored : Warning< + "-fbounds-safety-adoption-mode without -fbounds-safety is " + "ignored">, + InGroup; +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Instrumentation Issue" in { def warn_profile_data_out_of_date : Warning< "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 1ca1f9fac2860..e9c08054e3d84 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -291,11 +291,13 @@ def : DiagGroup<"c++1z-compat-mangling", [CXX17CompatMangling]>; // Name of this warning in GCC. def NoexceptType : DiagGroup<"noexcept-type", [CXX17CompatMangling]>; +def VariadicMacroArgumentsOmitted : DiagGroup<"variadic-macro-arguments-omitted">; + // Warnings for C code which is not compatible with previous C standards. def CPre11Compat : DiagGroup<"pre-c11-compat">; def CPre11CompatPedantic : DiagGroup<"pre-c11-compat-pedantic", [CPre11Compat]>; -def CPre23Compat : DiagGroup<"pre-c23-compat">; +def CPre23Compat : DiagGroup<"pre-c23-compat", [VariadicMacroArgumentsOmitted]>; def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic", [CPre23Compat]>; def : DiagGroup<"pre-c2x-compat", [CPre23Compat]>; @@ -900,7 +902,7 @@ def VolatileRegisterVar : DiagGroup<"volatile-register-var">; def Visibility : DiagGroup<"visibility">; def ZeroLengthArray : DiagGroup<"zero-length-array">; def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">; -def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">; +def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments", [VariadicMacroArgumentsOmitted]>; def MisleadingIndentation : DiagGroup<"misleading-indentation">; def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">; @@ -1193,7 +1195,7 @@ def CXX17 : DiagGroup<"c++17-extensions", [CXX17Attrs]>; // A warning group for warnings about using C++20 features as extensions in // earlier C++ versions. -def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs]>; +def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs, VariadicMacroArgumentsOmitted]>; // A warning group for warnings about using C++23 features as extensions in // earlier C++ versions. @@ -1220,7 +1222,7 @@ def C11 : DiagGroup<"c11-extensions">; def C99 : DiagGroup<"c99-extensions", [C99Designator]>; // A warning group for warnings about using C23 features as extensions. -def C23 : DiagGroup<"c23-extensions">; +def C23 : DiagGroup<"c23-extensions", [VariadicMacroArgumentsOmitted]>; def : DiagGroup<"c2x-extensions", [C23]>; @@ -1477,6 +1479,46 @@ def FunctionMultiVersioning def NoDeref : DiagGroup<"noderef">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafetyIncompleteArray : DiagGroup<"bounds-safety-incomplete-array">; +def BoundsSafetySingleToCount : DiagGroup<"bounds-safety-single-to-count">; +def BoundsSafetyInitListPartialNull : DiagGroup<"bounds-safety-init-list-partial-null">; +def BoundsSafetyInitList : DiagGroup<"bounds-safety-init-list">; +def BoundsAttributesExternArrayCount : DiagGroup<"bounds-attributes-extern-array-count">; +def BoundsAttributesInitSideEffect + : DiagGroup<"bounds-attributes-init-list-side-effect">; +def BoundsAttributesCXXExperimentalIgnored + : DiagGroup<"bounds-attributes-cxx-experimental-ignored">; +def BoundsAttributesObjCExperimentalIgnored + : DiagGroup<"bounds-attributes-objc-experimental-ignored">; +def BoundsSafetyRelaxedSystemHeadersIgnored + : DiagGroup<"bounds-safety-relaxed-system-headers-ignore">; +def BoundsAttributesImplicitConvSingleToExplicitIndexable : + DiagGroup< + "bounds-attributes-implicit-conversion-single-to-explicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexable : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-to-larger-type">; +// FIXME: This name is wrong but we have to keep it because some adopter's +// build system currently expects (rdar://119832922). +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToDynamicCountPtr : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-at-call-site">; +def BoundsSafetyConvertSingleToIndexableBoundsTruncated + : DiagGroup<"bounds-safety-single-to-indexable-bounds-truncated">; +def BoundsSafetyRedundantAttribute : DiagGroup<"bounds-attributes-redundant">; +def BoundsSafetyNullability : DiagGroup<"bounds-safety-nullability">; +def BoundsSafetyAdoptionModeIgnored + : DiagGroup<"bounds-safety-adoption-mode-ignored">; +def BoundsSafetyStrictTerminatedByCast + : DiagGroup<"bounds-safety-strict-terminated-by-cast">; +def BoundsSafetyCountAttrArithConstantCount : + DiagGroup<"bounds-safety-externally-counted-ptr-arith-constant-count">; +// TO_UPSTREAM(BoundsSafety) OFF + // -fbounds-safety and bounds annotation related warnings def BoundsSafetyCountedByEltTyUnknownSize : DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">; diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 5e5809f371cc5..bdaebbd57fa3d 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -39,7 +39,8 @@ namespace clang { DIAG_SIZE_AST = 300, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 4500, + // TO_UPSTREAM(BoundsSafety) + DIAG_SIZE_SEMA = 4500 + 240, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, DIAG_SIZE_INSTALLAPI = 100, diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 2351417487461..f27acbe64f05c 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -483,14 +483,14 @@ def ext_embedded_directive : Extension< InGroup>; def ext_c_missing_varargs_arg : Extension< "passing no argument for the '...' parameter of a variadic macro is " - "a C23 extension">, InGroup; + "a C23 extension">, InGroup; def ext_cxx_missing_varargs_arg : Extension< "passing no argument for the '...' parameter of a variadic macro is " - "a C++20 extension">, InGroup; + "a C++20 extension">, InGroup; def warn_c17_compat_missing_varargs_arg : Warning< "passing no argument for the '...' parameter of a variadic macro is " "incompatible with C standards before C23">, - InGroup, DefaultIgnore; + InGroup, DefaultIgnore; def warn_cxx17_compat_missing_varargs_arg : Warning< "passing no argument for the '...' parameter of a variadic macro is " "incompatible with C++ standards before C++20">, @@ -1002,6 +1002,15 @@ def err_pp_eof_in_assume_nonnull : Error< } +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_pp_abi_ptr_attr_set_syntax : Error< + "expected 'set(attr1 [attr2 ...])'">; + +} +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Dependency Directive Source Scanner Issue" in { def err_dep_source_scanner_missing_semi_after_at_import : Error< diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index 6d0c1b14acc12..ba8ce8f2e6d4f 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -98,6 +98,8 @@ VALUE_DIAGOPT(TabStop, 32, DefaultTabStop) /// The distance between tab stops. VALUE_DIAGOPT(MessageLength, 32, 0) DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0) +// TO_UPSTREAM(BoundsSafety) +DIAGOPT(BoundsSafetyAdoptionMode, 1, 0) #undef DIAGOPT #undef ENUM_DIAGOPT diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index af30e5f3fc3fa..90abfb0942a6f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3355,6 +3355,9 @@ def warn_attribute_return_pointers_refs_only : Warning< def warn_attribute_pointer_or_reference_only : Warning< "%0 attribute only applies to a pointer or reference (%1 is invalid)">, InGroup; +def warn_attribute_pointer_or_reference_or_record_only : Warning< + "%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">, + InGroup; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< @@ -4314,6 +4317,16 @@ def note_reference_is_return_value : Note<"%0 returns a reference">; def note_pointer_declared_here : Note< "pointer %0 declared here">; +/* TO_UPSTREAM(BoundsSafety) ON */ +def note_pointer_declared_here_quoted : Note< + "pointer '%0' declared here">; +def note_pointer_initialized_here : Note< + "pointer %0 initialized here">; +def note_pointer_assigned_here : Note< + "pointer %0 assigned here">; +def note_unnamed_pointer_declared_here : Note< + "pointer declared here">; +/* TO_UPSTREAM(BoundsSafety) OFF */ def warn_division_sizeof_ptr : Warning< "'%0' will return the size of the pointer, not the array itself">, InGroup>; @@ -4407,6 +4420,8 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara def note_previous_attribute : Note<"previous attribute is here">; def note_conflicting_attribute : Note<"conflicting attribute is here">; def note_attribute : Note<"attribute is here">; +// TO_UPSTREAM(BoundsSafety) +def note_named_attribute : Note<"%0 attribute is here">; def err_mismatched_ms_inheritance : Error< "inheritance model does not match %select{definition|previous declaration}0">; def warn_ignored_ms_inheritance : Warning< @@ -12242,6 +12257,838 @@ def warn_target_clone_no_impact_options : Warning<"version list contains entries that don't impact code generation">, InGroup; +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_bounds_safety_conflicting_pointer_attributes : Error< + "%select{array|pointer}0 cannot have more than one %select{bound|type|count|end|terminator}1 attribute">; +def note_bounds_safety_conflicting_pointer_attribute_args : Note< + "conflicting arguments for %select{count|end|terminator}0 were '%1' and '%2'">; +def warn_bounds_safety_duplicate_pointer_attributes : Warning< + "%select{array|pointer}0 annotated with %select{__unsafe_indexable|__bidi_indexable|__indexable|__single|__terminated_by}1 " + "multiple times. Annotate only once to remove this warning">, InGroup; +def err_bounds_safety_complete_array_with_count : Error< + "arrays with an explicit size decay to counted pointers and cannot also have " + "a count attribute">; +def warn_bounds_safety_promoting_incomplete_array_without_count : Warning< + "accessing elements of an unannotated incomplete array always fails at runtime">, InGroup; +def err_bounds_safety_conflicting_count_bound_attributes : Error< + "pointer cannot be %0 and '__%select{bidi_indexable|indexable}1' at the same time">; +def err_bounds_safety_end_pointer_single : Error< + "end-pointer must be '__single'">; +def err_bounds_safety_conflicting_count_range_attributes : Error< + "pointer cannot have count and range at the same time">; +def err_flexible_array_member_passed_by_copy : Error< + "-fbounds-safety forbids passing %0 by copy because it has a flexible array " + "member">; +def err_bounds_safety_dynamic_count_function_call : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0' attribute can only reference " + "function with 'const' attribute">; +def err_bounds_safety_dynamic_count_function_call_argument : Error< + "argument of function call '%0' in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute is not a constant expression">; +def err_attribute_invalid_argument_expression_for_pointer_bounds_attribute : Error< + "invalid argument expression to bounds attribute">; +def err_invalid_decl_kind_bounds_safety_dynamic_count : Error< + "count expression %select{on struct field|in function declaration}0 may only " + "reference %select{other fields of the same struct|parameters of that function}0">; +def err_invalid_decl_kind_bounds_safety_union_count : Error< + "cannot use %0 on union fields">; +def err_multiple_coupled_decls_in_bounds_safety_dynamic_count : Error< + "multiple coupled declarations in a -fbounds-safety attribute are not supported yet">; +def err_attribute_argument_type_for_bounds_safety_count : Error< + "%0 attribute requires an integer type argument">; +def err_attribute_argument_type_for_bounds_safety_range : Error< + "%0 attribute requires a pointer type argument">; +def err_bounds_safety_function_pointers_cannot_be_indexable : Error< + "function pointers cannot be indexable">; +def err_bounds_safety_voidptr_must_use_byte_count : Error< + "void pointers must use a byte count attribute instead of an item count">; +def err_bounds_safety_array_decay_to_single : Error< + "parameter of array type %0 decays to a __single pointer, and will not allow " + "arithmetic">; +def note_bounds_safety_array_decay_use_count_annotation : Note< + "add a count attribute within the declarator brackets or convert the " + "parameter to a pointer with a count or size attribute">; +def err_nested_bounds_safety_pointer_attribute_mismatch : Error< + "%select{%diff{assigning to $ from $|assigning to incompatible nested pointer type}0,1" + "|%diff{passing $ to parameter of incompatible nested pointer type $|" + "passing to parameter of incompatible nested pointer type}0,1" + "|%diff{returning $ from a function with result of incompatible nested pointer type $|" + "returning from function with return of incompatible nested pointer type}0,1" + "|%diff{converting $ to incompatible nested pointer type $|" + "converting between incompatible nested pointer types}0,1" + "|%diff{initializing $ with an expression of incompatible nested pointer type $|" + "initializing with expression of incompatible nested pointer type}0,1" + "|%diff{sending $ to parameter of incompatible nested pointer type $|" + "sending to parameter of incompatible nested pointer type}0,1" + "|%diff{casting $ to incompatible nested pointer type $|" + "casting between incompatible nested pointer types}0,1}2; " + "use explicit cast to perform this conversion">; +def err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch : Error< + "conditional expression evaluates %select{values with incompatible nested pointer types" + "|functions with incompatible bound attributes|values with mismatching __terminated_by attributes}2" + "%diff{ $ and $|}0,1">; +def err_typecheck_cond_incompatible_pointers : Error< + "conditional expression evaluates values with incompatible pointee types" + "%diff{ $ and $|}0,1; use explicit casts to perform this conversion">; +def err_bounds_safety_non_to_pointer : Error< + "non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use " + "'__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'">; +def err_incompatible_bounds_safety_function_pointer : Error< + "conversion between pointers to functions with incompatible bound attributes">; +def err_bounds_safety_unsafe_to_safe : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' " + "or '__unsafe_forge_bidi_indexable' to perform this conversion">; +// %2 is AssignmentAction enum +def err_bounds_safety_incomplete_single_to_indexable : Error< + "cannot %select{" + "assign to indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // assign + "pass __single pointer to incomplete type argument %0 when parameter is an indexable pointer %1; consider making the%select{| '%3'}4 parameter '__single'|" // pass + "return __single pointer to incomplete type %0 when return type is an indexable pointer %1; consider making the return type '__single'|" // return + "convert __single pointer to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // convert + "initialize indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // initialize + "send __single to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // send + "cast from __single pointer to incomplete type %0 to indexable pointer type %1" // cast + "}2">; +def warn_bounds_safety_implicit_conv_single_to_explicit_indexable : Warning< + "%select{" + "%diff{assigning to%select{ __indexable| __bidi_indexable|}3 type $ from%select{ __single|}4 type $|" + "assigning%select{ __indexable| __bidi_indexable|}3 type from%select{ __single|}4 type}0,1|" + "%diff{passing%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "passing%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{returning%select{ __single|}4 type $ from a function with%select{ __indexable| __bidi_indexable|}3 result type $|" + "returning%select{ __single|}4 type from a function with%select{ __indexable| __bidi_indexable|}3 result type}0,1|" + "%diff{converting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "converting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{initializing%select{ __indexable| __bidi_indexable|}3 type $ with an expression of%select{ __single|}4 type $|" + "initializing%select{ __indexable| __bidi_indexable|}3 type with an expression of%select{ __single|}4 type}0,1|" + "%diff{sending%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "sending%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{casting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "casting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "}2 results in %select{an __indexable|a __bidi_indexable}5 pointer that will trap if a non-zero offset " + "is dereferenced%select{|. consider adding '__counted_by' to '%7'}6">, + InGroup; +def warn_bounds_safety_conv_single_to_implicit_indexable : Warning< + "%select{indexing into|pointer arithmetic over}1 a __bidi_indexable local " + "variable %2 %select{assigned|initialized}3 from __single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %4 results in %select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}1" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}1 is >= %6|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}1 is >= %6 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}1 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}5">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_multiple_assignments : + Warning< "%select{indexing into|pointer arithmetic over}0 __bidi_indexable " + "local variable %1 that is assigned from a __single pointer results in " + "%select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}0" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}0 is >= %3|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}0 is >= %3 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}0 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}2">, + InGroup; +def note_single_entity_assigned_here : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafe_zeroth_element : + Warning<"%select{" + "indexing|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "dereferencing|" // UnsafeOpKind::Deref + "accessing field %5 through|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0 __bidi_indexable '%1'" + "%select{" + " at any index|||||||}0 " + "will %select{" + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Deref + "always trap|" // UnsafeOpKind::MemberAccess + "propagate an out-of-bounds pointer|" // UnsafeOpKind::Assignment + "return an out-of-bounds pointer|" // UnsafeOpKind::Return + "pass an out-of-bounds pointer|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0. At runtime %4 is assigned a __single pointer that" + " results in '%1' having bounds smaller than a single %2 (%3 byte%s3)">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast : + Warning< + "%select{explicit|implicit}0 cast of __bidi_indexable %1 to a larger pointee" + " type creates an out-of-bounds pointer. Later uses of the result may trap">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast_to_single_trap : + Warning< + "%select{explicit|implicit}0 cast of out-of-bounds __bidi_indexable to " + "__single will trap in a future compiler version due to the bounds of %1 " + "being too small to access a single element of type %2">, + InGroup; + +def note_single_entity_assigned_here_with_pointee_sizes : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 %select{assigned to|used to initialize}9 %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes) but %select{'%8'|cast of %2 to %8}7 has pointee type %5 (%6 bytes)">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_dynamic_count_conversion : + Warning< + "%select{" + "UNUSED|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "UNUSED|" // UnsafeOpKind::Deref + "UNUSED|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "casting" // // UnsafeOpKind::Cast + "}0 __bidi_indexable local variable %1 will %select{" + "UNUSED|" // WillTrapKind::NoTrap + "likely trap|" // WillTrapKind::Unknown + "trap|" // WillTrapKind::Trap + "trap (unless %1 is null)|" // WillTrapKind::TrapIffPtrNotNull + "UNUSED" // WillTrapKind::TrapIffPtrNull + "}2%select{" + " |" + " in a future compiler version " + "}6" + "when converting to %3 due to %1 having the bounds of a __single pointer" + "%select{" + // Non-constant count + ". If %1 is non-null then any %select{" + "count|" // __counted_by(or_null) + "size" // __sized_by(or_null) + "}7 > %8 will trap" + "%select{" + // __counted_by/__sized_by + ". If %1 is null then any %select{count|size}7 != 0 will trap|" + // __counted_by_or_null/__sized_by_or_null + "" + "}5|" + // Constant count + "" + "}4">, + InGroup; + + +def note_single_entity_assigned_here_with_dyn_count_conversion: Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes)" + "%select{" + // non-constant count + "|" + // constant count + " but conversion of %2 to %6 requires %7 bytes or more" + "}5">; + +def err_bounds_safety_dynamic_bound_redeclaration : Error< + "conflicting '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by}0' " + "attribute with the previous %select{function|variable}1 declaration">; +def err_bounds_safety_nullable_fam : Error< + "%select{array objects|flexible array members}1 cannot be null; did you mean %select{__counted_by|__sized_by}0 instead?">; +def warn_bounds_safety_nullable_dynamic_count_nonnullable : Warning< + "combining %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and '_Nonnull'; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">, InGroup; +def warn_bounds_safety_nonnullable_dynamic_count_nullable : Warning< + "combining %select{'__counted_by'|'__sized_by'}0 with non-zero %select{count|size}0 (which cannot be null) and '_Nullable'; did you mean %select{'__counted_by_or_null'|'__sized_by_or_null'}0 instead?">, InGroup; +def err_bounds_safety_nullable_dynamic_count_nonnullable : Error< + "cannot combine %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and %1; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">; + +def warn_bounds_safety_single_bitcast_lose_bounds : Warning< + "casting %0 to %1 creates a '%select{__indexable|__bidi_indexable}2' pointer " + "%select{with bounds containing only one %3|with zero length due to %3 having unknown size)}4">, + InGroup; + +def note_bounds_safety_single_bitcast_lose_bounds_keep_bound : Note< + "cast to %0 first to keep bounds of %1">; + +def note_bounds_safety_single_bitcast_lose_bounds_silence : Note< + "silence by making the destination '__single'">; + +def err_bounds_safety_atomic_unsupported_attribute : Error< + "_Atomic on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by|end}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_unsupported_attribute : Error< + "atomic operation on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_arithmetic_needs_unsafe_pointer : Error< + "address argument to atomic arithmetic operation must be a pointer to " + "'__unsafe_indexable' pointer (%0 invalid)">; + +def err_bounds_safety_typeof_dbpt : Error< + "__typeof__ on an expression of type %0 is not yet supported">; + +def err_bounds_safety_single_pointer_arithmetic : Error< + "pointer arithmetic on single pointer '%0' is out of bounds%select{|; consider adding '__counted_by' to '%2'}1">; +def err_bounds_safety_flexible_array_member_record_pointer_arithmetic : Error< + "-fbounds-safety forbids arithmetic on pointers to types with a flexible array " + "member">; +def err_bounds_safety_member_reference_null_base : Error< + "base of member reference is a null pointer">; +def err_bounds_safety_counted_by_without_size : Error< + "cannot apply '__counted_by%select{|_or_null}2' attribute to %0 because %1 has unknown size" + "%select{|; did you mean to use '__sized_by%select{|_or_null}2' instead?}3">; +def err_bounds_safety_counted_by_on_incomplete_type_on_use : Error < + "cannot %select{" + "use '%1'|" // Generic expr + "call '%1'" // CallExpr + "}0 with%select{" + "|" // Generic expr + " return" // CallExpr + "}0 type %2 because the pointee type %3 is incomplete " + "and the '%4' attribute requires the pointee type be complete in this context; " + "consider providing a complete definition for %3 or using the " + "'__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_assign : Error < + "cannot %select{" + "assign to %select{object|'%1'}2 that has|" // AA_Assigning, + "pass argument to %select{parameter|parameter '%1'}2 that has|" // AA_Passing, + "return|" // AA_Returning, + "convert to|" // AA_Converting (UNUSED) + "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AA_Initializing, + "pass argument to parameter that has|" // AA_Sending (UNUSED) + "cast to|" // AA_Casting (UNUSED) + "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED) + "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute " + "requires the pointee type be complete when %select{" + "assigning|" // AA_Assigning, + "passing|" // AA_Passing, + "returning|" // AA_Returning, + "converting|" // AA_Converting (UNUSED) + "initializing|" // AA_Initializing, + "passing|" // AA_Sending (UNUSED) + "casting|" // AA_Casting (UNUSED) + "passing" // AA_Passing_CFAudited (UNUSED) + "}0; " + "consider providing a complete definition for %6 or using the " + "'__sized_by%select{|_or_null}7' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_func_def : Error< + "cannot apply '%0' attribute to %select{" + "return|" // return type + "parameter%select{" // parameter type + "|" // unnamed parameter + " '%3'" // named parameter + "}2 with" + "}1 type %4 on a function definition because the pointee type %5 is " + "incomplete; consider providing a complete definition for %5 before the " + "function body or using the '__sized_by%select{|_or_null}6' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_var_decl : Error< + "cannot apply '%0' attribute to%select{| tentative}1 variable definition " + "'%2' with type %3 because the pointee type %4 is " + "incomplete; consider providing a complete definition for %4 before this " + "definition or using the '__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_pointer_subscript : Error< + "array subscript on %select{single|'__terminated_by'}0 pointer '%1' " + "%select{must use a constant index of 0 to be in bounds|is not allowed}0">; +def err_bounds_safety_indexable_pointer_arithmetic : Error< + "decremented indexable pointer '%0' is out of bounds">; +def err_bounds_safety_indexable_pointer_subscript : Error< + "array subscript with a negative index on indexable pointer '%0' is out of bounds">; +def err_bounds_safety_terminated_by_pointer_arithmetic_dec : Error< + "cannot decrement '__terminated_by' pointer '%0'">; +def err_bounds_safety_terminated_by_pointer_arithmetic : Error< + "pointer arithmetic on '__terminated_by' pointer '%0' can only increase the " + "value by one">; + +def err_bounds_safety_dynamic_bound_pointer_unary_arithmetic : Error< + "%select{negative|positive}0 pointer arithmetic on " + "%select{'__counted_by' |'__sized_by' |'__counted_by_or_null' |'__sized_by_or_null' |end |}1pointer" + "%select{||||| that starts the '__ended_by' chain}1 always traps">; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_unary_arithmetic_constant_count : Warning< + "%select{negative|positive}0 pointer arithmetic on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup, DefaultError; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_binary_assign_constant_count : Warning< + "compound %select{subtraction|addition}0-assignment on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup, DefaultError; + +def err_bounds_safety_dependent_vars_assign_side_effect : Error< + "assignments to dependent variables should not have side effects between them">; +def err_bounds_safety_missing_dependent_var_assignment : Error< + "assignment to %select{%2 |}3'%0' requires corresponding assignment to %select{|%2 }3'%1'; " + "add self assignment '%1 = %1' if the value has not changed">; +def err_bounds_safety_no_preceding_fam_base_assignment : Error< + "assignment to '%0' requires an %select{|immediately preceding }2assignment to '%1' with a wide pointer%select{ immediately preceding the group of dependent field assignments|}2">; +def note_bounds_safety_flex_base_assign : Note< + "'%0' is initialized with a '__single' pointer">; +def note_bounds_safety_first_dep_assign: Note< + "group of dependent field assignments starts here, place assignment to '%0' before it">; +def err_bounds_safety_multiple_assignments_to_dynamic_bound_decl : Error< + "multiple consecutive assignments to a %select{dynamic count|dynamic count pointer|ranged pointer}0 %1 " + "must be simplified; keep only one of the assignments">; +def note_bounds_safety_previous_assignment : Note< + "previously assigned here">; +def err_bounds_safety_non_adjacent_dependent_var_decl : Error< + "local variable %0 must be declared right next to its dependent decl">; +def err_bounds_safety_dependent_out_count_assign : Error< + "not allowed to change out parameter used as dependent count expression of other parameter or return type">; +def err_bounds_safety_dependent_out_count_buf_assign : Error< + "not allowed to change out parameter with dependent count">; +def err_bounds_safety_read_only_dynamic_count_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute depending " + "on an indirect count is implicitly read-only">; +def err_bounds_safety_read_only_dynamic_range_pointer : Error< + "parameter '%0' %select{with '__ended_by' attribute depending " + "on an indirect end pointer|referred to by an indirect '__ended_by' pointer}1 " + "is implicitly read-only">; +def err_bounds_safety_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' attribute " + "depending on an indirect count is implicitly read-only and cannot be passed " + "as an indirect argument">; +def err_bounds_safety_cannot_pass_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only and cannot be passed as an indirect " + "argument due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_deref_in_bounds_safety_count_non_parm_decl : Error< + "dereference operator in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' is only allowed for function parameters">; +def err_bounds_safety_dynamic_bound_arg_different_scope : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration from a different scope">; +def err_bounds_safety_dynamic_bound_arg_different_lifetime : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration of a different lifetime">; +def err_bounds_safety_global_dynamic_bound : Error< + "pointer with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "and the argument of the attribute must be defined in the same translation unit">; +def warn_bounds_safety_extern_array_dynamic_count : Warning< + "array with '%select{__counted_by|__sized_by}0' " + "and the argument of the attribute should be defined in the same translation unit">, + InGroup; +def err_bounds_safety_local_dependent_count_block : Error< + "local dependent count decl should be declared side by side to its dependent pointer">; +def err_bounds_safety_external_bound_share : Error< + "%select{variable|field}1 %0 referred to by %select{%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}2 variable|flexible array member}1 cannot be used in other dynamic bounds attributes">; +def err_bounds_safety_nested_dynamic_bound : Error< + "%0 attribute on nested pointer type is only allowed on indirect parameters">; +def err_bounds_safety_nested_dynamic_range : Error< + "%0 is not allowed for nested pointers">; +def err_bounds_safety_increment_dynamic_count : Error< + "incrementing '%0' without updating '%1' always traps">; +def err_bounds_safety_increment_dynamic_count_nodep : Error< + "incrementing '%0' always traps">; +def err_bounds_safety_dynamic_bound_pointer_compound_assign_side_effect : Error< + "compound assignment on dynamic bound pointer type %0 with side effects is not yet supported;" + "instead, use seperate assignment and binary expressions">; +def err_bounds_safety_dependent_assignments_order : Error< + "cannot reference '%0' after it is changed during consecutive assignments">; +def note_bounds_safety_decl_assignment : Note< + "'%0' has been assigned here">; +def err_bounds_safety_dependent_struct_assignment : Error< + "cannot assign '%0' because it contains field %1 referred to by flexible array member %2">; +def note_bounds_safety_count_param_loc : Note< + "referred to by count parameter here">; +def err_bounds_safety_dependent_field_duplicates : Error< + "cannot depend on nested field %0 because it exists in multiple instances in struct %1">; +def note_bounds_safety_struct_fields_only_in_fam : Note< + "nested struct member in count parameter only supported for flexible array members">; +def error_bounds_safety_no_arrow_members : Error< + "arrow notation not allowed for struct member in count parameter">; +def error_bounds_safety_no_count_in_unions : Error< + "count parameter refers to union '%0' of type %1">; + +def err_bounds_safety_taking_address_dynamic_bound_dependent : Error< + "%select{variable|field}1 referred to by '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "cannot be pointed to by any other variable; exception is when the pointer " + "is passed as a compatible argument to a function">; +def err_bounds_safety_taking_address_dynamic_bound_pointer : Error< + "%select{pointer|array}1 with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' cannot be " + "pointed to by any other variable; exception is when the variable is passed " + "as a compatible argument to a function">; + +def err_bounds_safety_incompatible_dynamic_count_argument: Error< + "incompatible dynamic count pointer argument to parameter of type %0">; +def err_bounds_safety_incompatible_indirect_count_parameter: Error< + "passing%select{| address of}1 %0 referred to by " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2' to a parameter that is not referred to " + "by the same attribute">; +def err_bounds_safety_incompatible_dynamic_bound_argument: Error< + "type of %0, %1, is incompatible with parameter of type %2">; +def err_bounds_safety_mismatching_count_type_argument: Error< + "incompatible pointer types assigning %0 with an expression with mismatching size attributes %1">; +def err_bounds_safety_incompatible_count_expression: Error< + "incompatible count expression (%0) vs. (%1) in argument to function">; +def err_bounds_safety_unsynchronized_indirect_param: Error< + "passing%select{| address of}1 %0 as an indirect parameter; " + "must also pass %2 %select{|or its address }3because the type of %select{%0|%2}4, " + "%5, refers to %select{%2|%0}4">; + +def err_bounds_safety_dynamic_count_negative : Error< + "negative %select{count|size}1 value of %2 for%select{| '%4' of type}3 %0">; +def err_bounds_safety_dynamic_count_from_array_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from array %3 (which has %5 %select{elements|bytes}2)|" + "passing array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning array %3 (which has %5 %select{elements|bytes}2) from a function with result type %1 and %select{count|size}2 value of %4|" + "converting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with array %3 (which has %5 %select{elements|bytes}2)|" + "sending array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_unbounded_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from %3%select{| with pointee of size %5}2|" + "passing %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning %3%select{| with pointee of size %5}2 from a function with result type %1 and %select{count|size}2 value of %4|" + "converting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with %3%select{| and pointee of size %5}2|" + "sending %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_null_nonzero_count : Error< + "%select{" + "assigning null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning null from a function with result type %1 and %select{count|size}2 value of %3|" + "converting null to type %1 with %select{count|size}2 value of %3|" + "%select{|implicitly }6initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with null|" + "sending null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting null to type %1 with %select{count|size}2 value of %3" + "}0 always fails">; + +def warn_bounds_safety_dynamic_count_from_nonnull_implicit_zero_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%4' of type}3 %1 with implicit %select{count|size}2 value of 0|" + "passing non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "returning non-null from a function with result type %1 and implicit %select{count|size}2 value of 0|" + "converting non-null to type %1 with implicit %select{count|size}2 value of 0|" + "initializing%select{| '%4' of type}3 %1 and implicit %select{count|size}2 value of 0 with non-null|" + "sending non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "casting non-null to type %1 with implicit %select{count|size}2 value of 0" + "}0, which creates a non-dereferenceable pointer; " + "explicitly set %select{count|size}2 value to 0 to remove this warning">, + InGroup; +def warn_bounds_safety_dynamic_count_from_nonnull_negative_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning non-null from a function with result type %1 with %select{count|size}2 value of %3|" + "converting non-null to type %1 with %select{count|size}2 value of %3|" + "initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with non-null|" + "sending non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting non-null to type %1 with %select{count|size}2 value of %3" + "}0; explicitly %select{assign|pass|return|convert|initialize|send|cast}0 null to remove this warning">, + InGroup, DefaultError; + +def warn_bounds_safety_dynamic_count_from_unbounded : Warning< + "%select{count|size}2 value is not statically known: " + "%select{" + "assigning to%select{| '%6' of type}5 %1 from %4|" + "passing %4 to parameter%select{| '%6'}5 of type %1|" + "returning %4 from a function with result type %1|" + "converting %4 to type %1|" + "initializing%select{| '%6' of type}5 %1 with %4|" + "sending %4 to parameter%select{| '%6'}5 of type %1|" + "casting %4 to type %1" + "}0 is invalid for any %select{count other than 0 or 1|size greater than %7}2" + "%select{| unless the pointer is null}3">, + InGroup; +def note_bounds_safety_dynamic_count_from_unbounded_count_location : Note< + "%select{count|size}1 %select{assigned|passed|returned|converted|initialized|sent|cast}0 here">; +def note_bounds_safety_consider_adding_to : Note< + "consider adding '%1' to '%0'">; +def note_bounds_safety_consider_adding_to_return : Note< + "consider adding '%1' to %0 return type of '%2'">; + +def warn_bounds_safety_initlist_range_partial_null : Warning< + "%select{|implicitly }0initializing field %1 of type %2 to NULL while " + "'%3' is initialized with a value rarely succeeds">, InGroup; + +def err_bounds_safety_dynamic_bound_init_side_effect : Error< + "initalizer for %select{count|size|'__counted_by' pointer|'__sized_by' pointer|'__counted_by_or_null' pointer|'__sized_by_or_null' pointer|'__ended_by' pointer|end pointer}0 with side effects is not yet supported">; + +def warn_bounds_safety_struct_init_side_effect : Warning< + "initializer %0 has a side effect; this may lead to an unexpected result " + "because the evaluation order of initialization list expressions is " + "indeterminate">, InGroup; + +def err_bounds_safety_flexible_global_wrong_count : Error< + "flexible array member is initialized with %0 element%select{|s}2, but count value is " + "initialized to %1">; +def err_bounds_safety_flexible_global_not_counted : Error< + "flexible array member is initialized without a count">; +def err_bounds_safety_flexible_global_non_int_count_init : Error< + "count '%0' has non-integer value '%1' of type %2">; + +def err_bounds_safety_local_external_dynamic_count : Error< + "attribute %0 is not allowed for local variables with external storage">; +def err_bounds_safety_scope_dynamic_range : Error< + "attribute %0 is only allowed for struct fields or function parameters">; +def err_bounds_safety_typedef_dynamic_bound : Error< + "%0 inside typedef is only allowed for function type">; +def err_bounds_safety_sized_by_array : Error< + "%0 cannot apply to arrays: use 'counted_by' instead">; +def err_bounds_safety_forge_addr_type : Error< + "'__unsafe_forge_%select{bidi_indexable|single|terminated_by}0' requires a pointer, array " + "or integer address argument">; +def err_bounds_safety_forge_bidi_size_type : Error< + "'__unsafe_forge_bidi_indexable' requires an integer size argument">; +def err_bounds_safety_forge_negative : Error< + "negative %select{address|size}0 argument to '__unsafe_forge_" + "%select{bidi_indexable|single|terminated_by}1'">; + +def err_bounds_safety_abi_ptr_attr_invalid : Error< + "%0 cannot be set as a default pointer attribute">; + +def err_bounds_safety_no_bounds : Error< + "cannot extract the %select{lower|upper}0 bound of %1 because %select{" + "it is not a pointer|it has no bounds specification|" + "its bounds are not contained within the pointer}2">; + +def err_bounds_safety_ptr_with_bounds_in_assembly : Error< + "pointer with bounds cannot be used with inline assembly">; +def err_bounds_safety_dyn_count_in_assembly : Error< + "external count of a pointer cannot be used with inline assembly">; + +def err_bounds_safety_ptrauth_on_indexable_pointer : Error< + "pointer authentication is currently unsupported on indexable pointers">; + +def err_bounds_safety_auto_dynamic_bound : Error< + "passing %select{'__counted_by'|'__sized_by'|'__counted_by_or_null'|'__sized_by_or_null'|'__ended_by'|end}0 pointer " + "as __auto_type initializer is not yet supported">; + +def err_bounds_safety_unsupported_address_of_incomplete_array : Error< + "cannot take address of incomplete __counted_by array">; +def note_bounds_safety_remove_address_of_operator : Note< + "remove '&' to get address as %0 instead of %1">; +def err_bounds_safety_unsupported_address_of_incomplete_array_type : Error< + "pointer to incomplete __counted_by array type %0 not allowed; " + "did you mean to use a nested pointer type?">; +def err_bounds_safety_unsupported_address_of_incomplete_array_count : Error< + "cannot take address of field referred to by __counted_by on a flexible array member">; + +def err_bounds_safety_terminated_by_wrong_type : Error< + "'__terminated_by' attribute can be applied to pointers, constant-length " + "arrays or incomplete arrays">; +def err_bounds_safety_terminated_by_wrong_pointer_type : Error< + "'__terminated_by' attribute currently can be applied only to '__single' " + "pointers">; +def err_bounds_safety_terminated_by_wrong_elem_or_pointee_type : Error< + "%select{element|pointee}0 type of %select{array|pointer}0 with " + "'__terminated_by' attribute must be an integer or a non-wide pointer">; +def err_bounds_safety_terminated_by_empty_array : Error< + "'__terminated_by' attribute cannot be applied to empty arrays">; + +def err_bounds_safety_terminated_by_terminator_must_be_const : Error< + "terminator value is not a constant expression">; + +def err_bounds_safety_terminated_by_to_indexable_wrong_ptr_type : Error< + "pointer argument must be a '__terminated_by' pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_to_indexable_wrong_terminator : Error< + "pointer argument must be terminated by %0 (got %1)">; + +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_type : Error< + "pointer argument must be a safe pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_wrong_pointee_type : Error< + "pointee type of the pointer argument must be an integer or a non-wide " + "pointer">; +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_to_term_type : Error< + "pointer to terminator argument must be a pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_pointee_mismatch : Error< + "pointee types of the pointer and pointer to terminator arguments must be " + "the same">; + +def err_bounds_safety_incompatible_string_literal_to_terminated_by : Error< + "'__terminated_by' pointer converted from a string literal must be " + "NUL-terminated">; + +def note_fixit_null_terminated_to_indexable: Note<"consider using '%select{__null_terminated_to_indexable|__unsafe_null_terminated_to_indexable}0()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound %select{excludes|includes}0 the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable : Note<"consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable_ptr : Note<"consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time">; + +def err_bounds_safety_incompatible_terminated_by_terminators : Error< + "pointers with incompatible terminators " + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2">; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 requires a linear search for the terminator; use " + "'%select{__terminated_by_to_indexable|__null_terminated_to_indexable}3()' to perform this conversion explicitly">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 is an unsafe operation; use '%select{__unsafe_terminated_by_from_indexable|__unsafe_null_terminated_from_indexable}3()' " + "or '%select{__unsafe_forge_terminated_by|__unsafe_forge_null_terminated}3()' to perform this conversion">, InGroup, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Error; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that discards '__terminated_by' attribute is not allowed">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that adds '__terminated_by' attribute is not allowed">, InGroup, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Error; + +def err_bounds_safety_terminated_by_uninitialized : Error< + "array '%0' with '__terminated_by' attribute must be initialized">; +def err_bounds_safety_terminated_by_incomplete_array_empty_init : Error< + "incomplete array '%0' with '__terminated_by' attribute must be initialized " + "with at least one element">; +def err_bounds_safety_terminated_by_terminator_in_array_must_be_const : Error< + "terminator in array '%0' must be a compile-time constant">; +def err_bounds_safety_terminated_by_wrong_initializer_kind : Error< + "array '%0' with '__terminated_by' attribute must be initialized with a " + "string literal or an initializer list">; +def err_bounds_safety_terminated_by_terminator_mismatch : Error< + "array '%0' with '__terminated_by' attribute is initialized with an " + "incorrect terminator (expected: %1; got %2)">; + +def err_bounds_safety_irrecoverable_attr : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to attributed type %1 in this context%select{| due to the surrounding 'typeof' specifier}2">; +def err_bounds_safety_undeduced : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to an undeduced type">; +} // End of CategoryName = "BoundsSafety Pointer Attributes Issue" +// TO_UPSTREAM(BoundsSafety) OFF + // three-way comparison operator diagnostics def err_implied_comparison_category_type_not_found : Error< "cannot %select{use builtin operator '<=>'|default 'operator<=>'}1 " @@ -12511,6 +13358,13 @@ def note_safe_buffer_usage_suggestions_disabled : Note< def warn_unsafe_buffer_usage_in_container : Warning< "the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information">, InGroup, DefaultIgnore; +def warn_unsafe_count_attributed_pointer_argument : Warning< + "unsafe assignment to function parameter of count-attributed type">, + InGroup, DefaultIgnore; +def note_unsafe_count_attributed_pointer_argument : Note< + "consider using %select{|a safe container and passing '.data()' to the " + "parameter%select{| '%3'}2 and '.size()' to its dependent parameter '%4' or }0" + "'std::span' and passing '.first(...).data()' to the parameter%select{| '%3'}2">; #ifndef NDEBUG // Not a user-facing diagnostic. Useful for debugging false negatives in // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits). diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 4f015db6cb91c..bb3b50c23ddd4 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -86,6 +86,7 @@ FEATURE(attribute_overloadable, true) FEATURE(attribute_unavailable_with_message, true) FEATURE(attribute_unused_on_fields, true) FEATURE(attribute_diagnose_if_objc, true) +FEATURE(attribute_noescape_nonpointer, true) FEATURE(blocks, LangOpts.Blocks) FEATURE(c_thread_safety_attributes, true) FEATURE(cxx_exceptions, LangOpts.CXXExceptions) @@ -323,5 +324,12 @@ FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVT FEATURE(cuda_noinline_keyword, LangOpts.CUDA) EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates) +// TODO(BoundsSafety): Deprecate this feature name +FEATURE(bounds_attributes, LangOpts.BoundsSafety) +/* TO_UPSTREAM(BoundsSafety) ON*/ +FEATURE(bounds_safety, LangOpts.BoundsSafety) +FEATURE(bounds_safety_attributes, LangOpts.BoundsSafetyAttributes) +/* TO_UPSTREAM(BoundsSafety) OFF*/ + #undef EXTENSION #undef FEATURE diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 4cf15b9c4a03a..aef255aec1478 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -534,6 +534,15 @@ BENIGN_LANGOPT(CheckConstexprFunctionBodies, 1, 1, "be used in a constant expression.") LANGOPT(BoundsSafety, 1, 0, "Bounds safety extension for C") +/* TO_UPSTREAM(BoundsSafety) ON*/ +LANGOPT(BoundsSafetyAttributes, 1, 0, "Experimental bounds safety attributes") +LANGOPT(BoundsAttributesCXXExperimental, 1, 0, "Experimental bounds attributes for C++") +LANGOPT(BoundsAttributesObjCExperimental, 1, 0, "Experimental bounds attributes for Objective-C") +LANGOPT(BoundsSafetyRelaxedSystemHeaders, 1, 1, + "Relax bounds safety assignment rules in system headers") +ENUM_LANGOPT(BoundsSafetyBringUpMissingChecks, BoundsSafetyNewChecksMask, 6, BS_CHK_Default, + "Bring up new -fbounds-safety run-time checks") +/* TO_UPSTREAM(BoundsSafety) OFF*/ #undef LANGOPT #undef COMPATIBLE_LANGOPT diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 91f1c2f2e6239..5bc33b88e63a2 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -408,6 +408,26 @@ class LangOptionsBase { IncompleteOnly = 3, }; + /* TODO(BoundsSafety) Deprecate the flag */ + enum BoundsSafetyNewChecksMask { + BS_CHK_None = 0, + + BS_CHK_AccessSize = 1 << 0, // rdar://72252593 + BS_CHK_IndirectCountUpdate = 1 << 1, // rdar://98749526 + BS_CHK_ReturnSize = 1 << 2, // rdar://83900556 + BS_CHK_EndedByLowerBound = 1 << 3, // rdar://91596663 + BS_CHK_CompoundLiteralInit = 1 << 4, // rdar://110871666 + BS_CHK_LibCAttributes = 1 << 5, // rdar://84733153 + + BS_CHK_All = BS_CHK_AccessSize | BS_CHK_IndirectCountUpdate | + BS_CHK_ReturnSize | BS_CHK_EndedByLowerBound | + BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes, + + // This sets the default value assumed by clang if no + // `-fbounds-safety-bringup-missing-checks` flags are passed to clang. + BS_CHK_Default = BS_CHK_None, + }; + /// Controls the various implementations for complex multiplication and // division. enum ComplexRangeKind { @@ -760,6 +780,36 @@ class LangOptions : public LangOptionsBase { return FPExceptionModeKind::FPE_Ignore; return EM; } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // Returns true iff -fbounds-safety is enabled. This is a convenience function + // that can be called from Xcode disclosed clang without hitting a compilation + // error (unlike directly accessing the `BoundsSafety` field). + bool hasBoundsSafety() const { + return BoundsSafety; + } + + // Returns true iff -fbounds-safety-attributes is enabled. This is a + // convenience function that can be called from Xcode disclosed clang without + // hitting a compilation error (unlike directly accessing the + // `BoundsSafetyAttributes` field). + bool hasBoundsSafetyAttributes() const { + return BoundsSafetyAttributes; + } + + // Returns true iff we are compiling in bounds-safety's attribute-only mode. + bool isBoundsSafetyAttributeOnlyMode() const { + return hasBoundsSafetyAttributes() && !hasBoundsSafety(); + } + + // Take `enum BoundsSafetyNewChecksMask` value as input and return true if + // the mask is set. New -fbounds-safety run-time checks should use this helper + // to decide whether to enable/disable the checks. + bool hasNewBoundsSafetyCheck(BoundsSafetyNewChecksMask ChkMask) const { + return (BoundsSafetyBringUpMissingChecks & ChkMask) != 0; + } + + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// Floating point control options diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 9bf23fae50a9e..4489db576759f 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -216,6 +216,19 @@ def SEHFinallyStmt : StmtNode; def SEHLeaveStmt : StmtNode; def MSDependentExistsStmt : StmtNode; +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety Extensions. +def AssumptionExpr : StmtNode; +def BoundsSafetyPointerPromotionExpr : StmtNode; +def ForgePtrExpr : StmtNode; +def GetBoundExpr : StmtNode; +def PredefinedBoundsCheckExpr : StmtNode; +def BoundsCheckExpr : StmtNode; +def MaterializeSequenceExpr : StmtNode; +def TerminatedByToIndexableExpr : StmtNode; +def TerminatedByFromIndexableExpr : StmtNode; +// TO_UPSTREAM(BoundsSafety) OFF + // OpenCL Extensions. def AsTypeExpr : StmtNode; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 1a3f5b25e4711..517ef718c9598 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -797,6 +797,17 @@ KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) // Clang-specific keywords enabled only in testing. TESTING_KEYWORD(__unknown_anytype , KEYALL) +/* TO_UPSTREAM(BoundsSafety) ON */ +// Bounds-safety extensions. +KEYWORD(__builtin_unsafe_forge_bidi_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_forge_single , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_unsafe_forge_terminated_by , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_get_pointer_lower_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_get_pointer_upper_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_from_indexable, KEYBOUNDSSAFETY) +/* TO_UPSTREAM(BoundsSafety) OFF */ //===----------------------------------------------------------------------===// // Objective-C @-preceded keywords. diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index fee49cf4326df..4f20727d23bf3 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -111,6 +111,10 @@ def ObjCInterfaceType : TypeNode, LeafType; def ObjCObjectPointerType : TypeNode; def BoundsAttributedType : TypeNode; def CountAttributedType : TypeNode, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) ON +def DynamicRangePointerType : TypeNode, NeverCanonical; +def ValueTerminatedType : TypeNode, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) OFF def PipeType : TypeNode; def AtomicType : TypeNode; def BitIntType : TypeNode; diff --git a/clang/include/clang/Driver/BoundsSafetyArgs.h b/clang/include/clang/Driver/BoundsSafetyArgs.h new file mode 100644 index 0000000000000..6de8f2df23895 --- /dev/null +++ b/clang/include/clang/Driver/BoundsSafetyArgs.h @@ -0,0 +1,25 @@ +//===- BoundsSafetyArgs.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#define LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Option/ArgList.h" + +namespace clang { +namespace driver { +using BoundsSafetyNewChecksMaskTy = + std::underlying_type::type; + +BoundsSafetyNewChecksMaskTy +ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine *Diags); +} // namespace driver +} // namespace clang + +#endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index f0400432b958e..16e95bdb44928 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1950,6 +1950,77 @@ defm bounds_safety : BoolFOption< BothFlags<[], [CC1Option], " experimental bounds safety extension for C">>; +// TO_UPSTREAM(BoundsSafety) ON +// Apple internal clients currently use `-fbounds-safety` so provide +// aliases to upstream's `-fexperimental-bounds-safety` flag. When +// upstream eventually drops the `experimental-` prefix these aliases +// can be removed. +def : Flag<["-"], "fbounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias, HelpText<"Enable bounds safety extension for C">; +def : Flag<["-"], "fno-bounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias, HelpText<"Disable bounds safety extension for C">; + +// Aliases for -fbounds-safety to remove once these are stripped from adopters +def : Flag<["-"], "fbounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias; +def : Flag<["-"], "fno-bounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias; +defm experimental_bounds_safety_attributes : BoolFOption< + "experimental-bounds-safety-attributes", + LangOpts<"BoundsSafetyAttributes">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], " experimental attributes">>; +defm bounds_attributes_cxx_experimental : BoolFOption< + "bounds-attributes-cxx-experimental", + LangOpts<"BoundsAttributesCXXExperimental">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for C++">>; +defm bounds_attributes_objc_experimental : BoolFOption< + "bounds-attributes-objc-experimental", + LangOpts<"BoundsAttributesObjCExperimental">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for ObjC">>; +defm bounds_safety_relaxed_system_headers : BoolFOption< + "bounds-safety-relaxed-system-headers", + LangOpts<"BoundsSafetyRelaxedSystemHeaders">, DefaultTrue, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " relaxed bounds safety rules for code in system headers">>; +defm bounds_safety_adoption_mode : BoolFOption<"bounds-safety-adoption-mode", + DiagnosticOpts<"BoundsSafetyAdoptionMode">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " generating improved -fbounds-safety diagnostics to aid engineers " + "adopting -fbounds-safety. This is off by default to avoid impacting " + "compile times">>; + +def fbounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fbounds-safety-bringup-missing-checks=">, Group, + MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, all)">; + +def fno_bounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fno-bounds-safety-bringup-missing-checks=">, Group, + MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Disable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, all)">; + +def fbounds_safety_bringup_missing_checks : Flag<["-"], "fbounds-safety-bringup-missing-checks">, Group, + Visibility<[ClangOption]>, + Alias, AliasArgs<["all"]>, + HelpText<"Enable new -fbounds-safety run-time checks">; + +def fno_bounds_safety_bringup_missing_checks : Flag<["-"], "fno-bounds-safety-bringup-missing-checks">, Group, + Visibility<[ClangOption]>, + Alias, AliasArgs<["all"]>, + HelpText<"Disable new -fbounds-safety run-time checks">; + defm addrsig : BoolFOption<"addrsig", CodeGenOpts<"Addrsig">, DefaultFalse, PosFlag, @@ -4064,6 +4135,24 @@ def ftrap_function_EQ : Joined<["-"], "ftrap-function=">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Issue call to specified function rather than a trap instruction">, MarshallingInfoString>; +/* TO_UPSTREAM(BoundsSafety) ON */ +defm trap_function_returns : BoolFOption<"trap-function-returns", + CodeGenOpts<"TrapFuncReturns">, DefaultFalse, + PosFlag, + NegFlag>; +defm unique_traps : BoolFOption<"unique-traps", + CodeGenOpts<"UniqueTrapBlocks">, DefaultFalse, + PosFlag, + NegFlag>; +/* TO_UPSTREAM(BoundsSafety) OFF */ def funroll_loops : Flag<["-"], "funroll-loops">, Group, HelpText<"Turn on loop unroller">, Visibility<[ClangOption, CC1Option]>; def fno_unroll_loops : Flag<["-"], "fno-unroll-loops">, Group, diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index 46cc564086f1c..ed94952114ee3 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -334,6 +334,14 @@ class PPCallbacks { /// is read. virtual void PragmaAssumeNonNullEnd(SourceLocation Loc) {} + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Callback invoked when a \#pragma clang abi_ptr_attr set (*ATTR*) + /// directive is read. + virtual void + PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) {} + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Called by Preprocessor::HandleMacroExpandedIdentifier when a /// macro invocation is found. virtual void MacroExpands(const Token &MacroNameTok, @@ -665,6 +673,15 @@ class PPChainedCallbacks : public PPCallbacks { Second->PragmaAssumeNonNullEnd(Loc); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) + override { + First->PragmaAbiPointerAttributesSet(Loc, Spec); + Second->PragmaAbiPointerAttributesSet(Loc, Spec); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange Range, const MacroArgs *Args) override { First->MacroExpands(MacroNameTok, MD, Range, Args); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 3342fbac35575..fe4501bd31b1b 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1373,10 +1373,12 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *MacroII = nullptr; SourceLocation AttrNameLoc; SmallVector Decls; + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel; explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name, - SourceLocation Loc) - : Self(P), AttrName(Name), AttrNameLoc(Loc) {} + SourceLocation Loc, unsigned Level = 0) + : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {} void ParseLexedAttributes() override; @@ -1696,7 +1698,9 @@ class Parser : public CodeCompletionHandler { void SkipFunctionBody(); Decl *ParseFunctionDefinition(ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), - LateParsedAttrList *LateParsedAttrs = nullptr); + LateParsedAttrList *LateParsedAttrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); void ParseKNRParamDeclarations(Declarator &D); // EndLoc is filled with the location of the last token of the simple-asm. ExprResult ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc); @@ -2017,6 +2021,36 @@ class Parser : public CodeCompletionHandler { /// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast. ExprResult ParseBuiltinBitCast(); + /* TO_UPSTREAM(BoundsSafety) ON */ + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_bidi_indexable(expr, size) + ExprResult ParseUnsafeForgeBidiIndexable(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_single(expr) + ExprResult ParseUnsafeForgeSingle(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_terminated_by(expr, terminator) + ExprResult ParseUnsafeForgeTerminatedBy(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_get_pointer_lower_bound(expr) + enum PointerBoundKind { PBK_Lower, PBK_Upper }; + ExprResult ParseGetPointerBound(PointerBoundKind K); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: + /// __builtin_terminated_by_to_indexable(pointer [, terminator]) + /// __builtin_unsafe_terminated_by_to_indexable(pointer [, terminator]) + ExprResult ParseTerminatedByToIndexable(bool Unsafe); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_terminated_by_from_indexable(terminator, + /// pointer [, pointer-to-terminator]) + ExprResult ParseUnsafeTerminatedByFromIndexable(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Type Identification ExprResult ParseCXXTypeid(); @@ -2479,7 +2513,8 @@ class Parser : public CodeCompletionHandler { ParsedAttributes &Attrs, ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd = nullptr, - ForRangeInit *FRI = nullptr); + ForRangeInit *FRI = nullptr, + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); bool ParseAsmAttributesAfterDeclarator(Declarator &D); @@ -2526,14 +2561,19 @@ class Parser : public CodeCompletionHandler { void ParseSpecifierQualifierList( DeclSpec &DS, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal) { - ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr) { + ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC, + LateAttrs); } void ParseSpecifierQualifierList( DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseObjCTypeQualifierList(ObjCDeclSpec &DS, DeclaratorContext Context); @@ -2790,7 +2830,9 @@ class Parser : public CodeCompletionHandler { ParseTypeName(SourceRange *Range = nullptr, DeclaratorContext Context = DeclaratorContext::TypeName, AccessSpecifier AS = AS_none, Decl **OwnedType = nullptr, - ParsedAttributes *Attrs = nullptr); + ParsedAttributes *Attrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); private: void ParseBlockId(SourceLocation CaretLoc); @@ -2945,11 +2987,15 @@ class Parser : public CodeCompletionHandler { void ParseGNUAttributes(ParsedAttributes &Attrs, LateParsedAttrList *LateAttrs = nullptr, Declarator *D = nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + // NestedTypeLevel is not a parameter in upstream void ParseGNUAttributeArgs(IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form, Declarator *D); + ParsedAttr::Form Form, Declarator *D, + size_t NestedTypeLevel=0); + /* TO_UPSTREAM(BoundsSafety) OFF */ IdentifierLoc *ParseIdentifierLoc(); unsigned @@ -3075,6 +3121,8 @@ class Parser : public CodeCompletionHandler { void ParseOpenCLQualifiers(ParsedAttributes &Attrs); void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs); void ParseCUDAFunctionAttributes(ParsedAttributes &attrs); + // TO_UPSTREAM(BoundsSafety) + void ParseBoundsSafetyTypeSpecifiers(ParsedAttributes &attrs); bool isHLSLQualifier(const Token &Tok) const; void ParseHLSLQualifiers(ParsedAttributes &Attrs); @@ -3129,19 +3177,27 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Form Form); - void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Upstream doesn't have the `Declarator` parameter + void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, + LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ void ParseBoundsAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form); + ParsedAttr::Form Form, + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel = 0); void ParseTypeofSpecifier(DeclSpec &DS); SourceLocation ParseDecltypeSpecifier(DeclSpec &DS); void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS, SourceLocation StartLoc, SourceLocation EndLoc); - void ParseAtomicSpecifier(DeclSpec &DS); + void ParseAtomicSpecifier(DeclSpec &DS, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); ExprResult ParseAlignArgument(StringRef KWName, SourceLocation Start, SourceLocation &EllipsisLoc, bool &IsType, @@ -3219,7 +3275,9 @@ class Parser : public CodeCompletionHandler { DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed, bool AtomicAllowed = true, bool IdentifierRequired = false, std::optional> CodeCompletionHandler = - std::nullopt); + std::nullopt, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseDirectDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); @@ -3238,16 +3296,21 @@ class Parser : public CodeCompletionHandler { void ParseParameterDeclarationClause( Declarator &D, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc) { + SourceLocation &EllipsisLoc, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr) { return ParseParameterDeclarationClause( D.getContext(), attrs, ParamInfo, EllipsisLoc, D.getCXXScopeSpec().isSet() && - D.isFunctionDeclaratorAFunctionDeclaration()); + D.isFunctionDeclaratorAFunctionDeclaration(), + LateParamAttrs); } void ParseParameterDeclarationClause( DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false); + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr); void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); @@ -3357,10 +3420,9 @@ class Parser : public CodeCompletionHandler { AccessSpecifier AS, ParsedAttributes &Attr, ParsedTemplateInfo &TemplateInfo, ParsingDeclRAIIObject *DiagsFromTParams = nullptr); - DeclGroupPtrTy - ParseCXXClassMemberDeclarationWithPragmas(AccessSpecifier &AS, - ParsedAttributes &AccessAttrs, - DeclSpec::TST TagType, Decl *Tag); + DeclGroupPtrTy ParseCXXClassMemberDeclarationWithPragmas( + AccessSpecifier &AS, ParsedAttributes &AccessAttrs, DeclSpec::TST TagType, + Decl *Tag); void ParseConstructorInitializer(Decl *ConstructorDecl); MemInitResult ParseMemInitializer(Decl *ConstructorDecl); void HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, @@ -3862,6 +3924,25 @@ class Parser : public CodeCompletionHandler { /// \param IncludeLoc The location at which this parse was triggered. TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Parse the given string as an expression in the argument position for a + /// bounds safety attribute. + /// + /// This is a dangerous utility function currently employed only by API notes. + /// It is not a general entry-point for safely parsing expressions from + /// strings. + /// + /// \param ExprStr The string to be parsed as an expression. + /// \param Context The name of the context in which this string is being + /// parsed, which will be used in diagnostics. + /// \param ParentDecl If a function or method is provided, the parameters are + /// added to the current parsing context. + /// \param IncludeLoc The location at which this parse was triggered. + ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr, + StringRef Context, + Decl *ParentDecl, + SourceLocation IncludeLoc); + /* TO_UPSTREAM(BoundsSafety) OFF */ //===--------------------------------------------------------------------===// // Modules diff --git a/clang/include/clang/Sema/BoundsSafetySuggestions.h b/clang/include/clang/Sema/BoundsSafetySuggestions.h new file mode 100644 index 0000000000000..037b0f0b0684c --- /dev/null +++ b/clang/include/clang/Sema/BoundsSafetySuggestions.h @@ -0,0 +1,147 @@ +/* TO_UPSTREAM(BoundsSafety) ON */ +//===- BoundsSafetySuggestions.h - -fbounds-safety suggestions --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a collection of analyses that aid adoption of +// -fbounds-safety annotations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H +#define LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H + +#include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/ArrayRef.h" +namespace clang { + +// Forward-declare to reduce bloat. +class ASTContext; +class Decl; +class Expr; +class FieldDecl; +class FunctionDecl; +class NamedDecl; +class ParmVarDecl; +class Sema; +class Stmt; +class VarDecl; +// An abstract interface to the bounds safety suggestion machine. +class BoundsSafetySuggestionHandler { +public: + enum class UnsafeOpKind { + Index, + Arithmetic, + Deref, + MemberAccess, + Assignment, + Return, + CallArg, + Cast + }; + + enum class AssignmentSourceKind { + Parameter = 0, + GlobalVar, + LocalVar, + FunctionCallReturnValue, + ArrayElement, + StructMember, + UnionMember, + }; + + enum class WillTrapKind { + NoTrap, + Unknown, + Trap, + TrapIffPtrNotNull, + TrapIffPtrNull + }; + + enum class PtrArithOOBKind { + NEVER_OOB = 0, + ALWAYS_OOB_BASE_OOB, + ALWAYS_OOB_CONSTANT_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO, + OOB_IF_EFFECTIVE_OFFSET_LT_ZERO, + UNKNOWN + }; + + struct SingleEntity { + AssignmentSourceKind Kind; + const NamedDecl *Entity; + const Expr *AssignmentExpr; + + // The bounds of the __bidi_indexable will store the bounds of a single + // `SinglePointeeTy`. Not necessarily the same as the + // `AssignmentExpr->getType()` due to additional casts. + const QualType SinglePointeeTy; + }; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// potentially unsafe pointer arithmetic or indexing) is initialized or + /// assigned from a an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + PtrArithOOBKind IsOOB, size_t MinimumPtrArithOOBOffset) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// an unsafe buffer operation that accesses the 0th element) is initialized + /// or assigned from an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, + const Expr *Operand, UnsafeOpKind Kind) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that is + /// "unsafely cast") is initialized + /// or assigned from an entity that is a __single pointer. + /// + /// The following types of cast are considered unsafe: + /// + /// 1. `__bidi_indexable` -> `__single` (CK_BoundsSafetyPointerCast) where + /// `local` has insufficient bounds to allow access to a single element of the + /// pointee type. + /// + /// 2. Bit cast where the pointee type of the pointer (CK_BitCast) is changed + /// to a larger type than `local` has the bounds for. I.e. the resulting + /// pointer is an out-of-bounds pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand) = 0; + + virtual void handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand, const QualType DCPT, WillTrapKind WillTrap, + std::optional ConstantCount, size_t MaxSafeSizeOrCount) = 0; + + // Always provide a virtual destructor! + virtual ~BoundsSafetySuggestionHandler() = default; +}; + +void checkBoundsSafetySuggestions(const Decl *D, + BoundsSafetySuggestionHandler &H, Sema &S); + +inline const StreamingDiagnostic & +operator<<(const StreamingDiagnostic &PD, + const BoundsSafetySuggestionHandler::AssignmentSourceKind Kind) { + PD.AddTaggedVal(static_cast(Kind), DiagnosticsEngine::ak_uint); + return PD; +} + +} // namespace clang + +#endif /* LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H */ +/* TO_UPSTREAM(BoundsSafety) OFF */ diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 425b6e2a0b30c..92830645e712a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1265,6 +1265,18 @@ struct DeclaratorChunk { ParsedAttributesView AttrList; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LateParsedAttrInfo { + CachedTokens Toks; + IdentifierInfo &AttrName; + SourceLocation AttrNameLoc; + + explicit LateParsedAttrInfo(CachedTokens Toks, IdentifierInfo &AttrName, + SourceLocation AttrNameLoc) + : Toks(Toks), AttrName(AttrName), AttrNameLoc(AttrNameLoc) {} + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/unaligned/atomic. LLVM_PREFERRED_TYPE(DeclSpec::TQ) @@ -1285,8 +1297,26 @@ struct DeclaratorChunk { /// The location of the __unaligned-qualifier, if any. SourceLocation UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; struct ReferenceTypeInfo { @@ -1317,7 +1347,26 @@ struct DeclaratorChunk { /// expression class on all clients, NumElts is untyped. Expr *NumElts; - void destroy() {} + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// ParamInfo - An array of paraminfo objects is allocated whenever a function @@ -1666,7 +1715,9 @@ struct DeclaratorChunk { SourceLocation VolatileQualLoc, SourceLocation RestrictQualLoc, SourceLocation AtomicQualLoc, - SourceLocation UnalignedQualLoc) { + SourceLocation UnalignedQualLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Pointer; I.Loc = Loc; @@ -1677,6 +1728,14 @@ struct DeclaratorChunk { I.Ptr.RestrictQualLoc = RestrictQualLoc; I.Ptr.AtomicQualLoc = AtomicQualLoc; I.Ptr.UnalignedQualLoc = UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Ptr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Ptr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Ptr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -1694,7 +1753,9 @@ struct DeclaratorChunk { /// Return a DeclaratorChunk for an array. static DeclaratorChunk getArray(unsigned TypeQuals, bool isStatic, bool isStar, Expr *NumElts, - SourceLocation LBLoc, SourceLocation RBLoc) { + SourceLocation LBLoc, SourceLocation RBLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Array; I.Loc = LBLoc; @@ -1703,6 +1764,14 @@ struct DeclaratorChunk { I.Arr.hasStatic = isStatic; I.Arr.isStar = isStar; I.Arr.NumElts = NumElts; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Arr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Arr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Arr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -2404,6 +2473,21 @@ class Declarator { return DeclTypeInfo[i]; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + /// Add all DeclaratorChunks from Other to the end of this declarator, and + /// remove them from Other. This transfers ownership of the DeclaratorChunks + /// and their attributes to this object. + void TakeTypeObjects(Declarator &Other) { + DeclTypeInfo.append(Other.DeclTypeInfo); // Keep the ordering + while (!Other.DeclTypeInfo.empty()) { + // Remove without calling destroy(), so it won't be destroyed when + // calling the destructor on Other + DeclaratorChunk Removed = Other.DeclTypeInfo.pop_back_val(); + getAttributePool().takeFrom(Removed.getAttrs(), Other.getAttributePool()); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + typedef SmallVectorImpl::const_iterator type_object_iterator; typedef llvm::iterator_range type_object_range; diff --git a/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h new file mode 100644 index 0000000000000..f1a64f01911a2 --- /dev/null +++ b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h @@ -0,0 +1,31 @@ +//===--- DynamicCountPointerAssignmentAnalysisExported.h --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file exports DynamicCountPointer Analysis utilities to other libraries. +// +// This is a workaround necessitated in the fact that Sema/TreeTransform.h is a +// private header file (not in the include path that other libraries can use). +// The "right" solution here would be to move the header so it is public but +// that would create a conflict with upstream which is really not desirable. +// +// So instead this file exports a really simple interface that hides the +// `TreeTransform` type (and its dependencies) +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#define LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#include "clang/Sema/Ownership.h" +#include "clang/Sema/Sema.h" + +namespace clang { + +ExprResult ReplaceCountExprParamsWithArgsFromCall(const Expr *CountExpr, + const CallExpr *CE, Sema &S); + +} + +#endif diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 22cbd0d90ee43..29ddfada0f937 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -191,6 +191,10 @@ class ParsedAttr final LLVM_PREFERRED_TYPE(bool) mutable unsigned IsPragmaClangAttribute : 1; + /// Determines if the attribute will be printed as macro in the diagnostics. + LLVM_PREFERRED_TYPE(bool) + mutable unsigned PrintAsMacro : 1; + /// The location of the 'unavailable' keyword in an /// availability attribute. SourceLocation UnavailableLoc; @@ -225,7 +229,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), Info(ParsedAttrInfo::get(*this)) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); } @@ -242,8 +246,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), UnavailableLoc(unavailable), + MessageExpr(messageExpr), Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) @@ -260,7 +264,8 @@ class ParsedAttr final NumArgs(3), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; @@ -276,7 +281,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -294,7 +300,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), Info(ParsedAttrInfo::get(*this)) { new (&getTypeBuffer()) ParsedType(typeArg); } @@ -306,7 +312,8 @@ class ParsedAttr final NumArgs(0), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); } @@ -493,9 +500,11 @@ class ParsedAttr final /// Set the macro identifier info object that this parsed attribute was /// declared in if it was declared in a macro. Also set the expansion location /// of the macro. - void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc) { + void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc, + bool PrintAsMac) { MacroII = MacroName; MacroExpansionLoc = Loc; + PrintAsMacro = PrintAsMac; } /// Returns true if this attribute was declared in a macro. @@ -511,6 +520,12 @@ class ParsedAttr final return MacroExpansionLoc; } + bool printAsMacro() const { + if (!hasMacroIdentifier()) + return false; + return PrintAsMacro; + } + /// Check if the attribute has exactly as many args as Num. May output an /// error. Returns false if a diagnostic is produced. bool checkExactlyNumArgs(class Sema &S, unsigned Num) const; @@ -1105,16 +1120,16 @@ enum AttributeDeclKind { inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr &At) { - DB.AddTaggedVal(reinterpret_cast(At.getAttrName()), + const IdentifierInfo *AttrName = + At.printAsMacro() ? At.getMacroIdentifier() : At.getAttrName(); + DB.AddTaggedVal(reinterpret_cast(AttrName), DiagnosticsEngine::ak_identifierinfo); return DB; } inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr *At) { - DB.AddTaggedVal(reinterpret_cast(At->getAttrName()), - DiagnosticsEngine::ak_identifierinfo); - return DB; + return DB << *At; } /// AttributeCommonInfo has a non-explicit constructor which takes an diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f0774c43b0958..b49c7f37afb27 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -228,6 +228,8 @@ class VisibleDeclConsumer; class IndirectFieldDecl; struct DeductionFailureInfo; class TemplateSpecCandidateSet; +// TO_UPSTREAM(BoundsSafety) +class CopyExpr; namespace sema { class AccessedEntity; @@ -367,6 +369,145 @@ class PreferredTypeBuilder { llvm::function_ref ComputeType; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Groups together the parameters to emit a bounds check. +class BoundsCheckBuilder { + // (the larger pointer we are slicing) + /// The wide pointer representing the range that is being sliced into. Either + /// it or both of WideLowerBound and WideUpperBound must be set. + Expr *WidePointerExpr; + + /// The lower bound of the range that is being sliced into. Either it and + /// WideUpperBound must be set, or WidePointerExpr must be set. + Expr *WideLowerBound; + + /// The upper bound of the range that is being sliced into. Either it and + /// WideLowerBound must be set, or WidePointerExpr must be set. + Expr *WideUpperBound; + + /// An index from the lower bound that is being accessed. At most one of it or + /// AccessStartIndex must be set. If neither is set, this is assumed to be + /// dereferencing the wide pointer. + Expr *AccessStartIndex; + + /// The start of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessStartIndex must be set. If + /// neither is set, this is assumed to be dereferencing the wide pointer. + Expr *AccessLowerBound; + + /// The end of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessCount must be set. If neither + /// is set, behaves as if AccessCount was the integer literal 1. + Expr *AccessUpperBound; + + /// The number of items being accessed, starting from AccessLowerBound. At + /// most one of it or AccessUpperBound must be set. If neither is set, behaves + /// as if AccessCount was the integer literal 1. + Expr *AccessCount; + + llvm::SmallVectorImpl &OVEs; + + /// If set to 1, AccessCount is a byte count. Must be 0 if AccessUpperBound is + /// set. + unsigned CountInBytes : 1; + /// If set to 1, the accessed bounds may be wider than the sliced bounds + /// (which are typically 0 when the sliced pointer is null). + unsigned OrNull : 1; + + OpaqueValueExpr *OpaqueWrap(Sema &S, Expr *E); + bool BuildIndexBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &Result); + bool BuildPtrBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &Result); + +public: + BoundsCheckBuilder(llvm::SmallVectorImpl &OVEs) + : WidePointerExpr(), WideLowerBound(), WideUpperBound(), + AccessStartIndex(), AccessLowerBound(), AccessUpperBound(), + AccessCount(), OVEs(OVEs), CountInBytes(0), OrNull(0) {} + + Expr *getWidePointer() { return WidePointerExpr; } + std::pair getWidePointerBounds() { + assert(!WideLowerBound == !WideUpperBound); + return std::make_pair(WideLowerBound, WideUpperBound); + } + + Expr *getAccessStartIndex() { return AccessStartIndex; } + Expr *getAccessLowerBound() { return AccessLowerBound; } + Expr *getAccessUpperBound() { return AccessUpperBound; } + Expr *getAccessCount() { return AccessCount; } + bool isCountInBytes() { assert(AccessCount); return CountInBytes; } + bool isOrNull() { return OrNull; } + + void setWidePointer(Expr *Wide) { + WidePointerExpr = Wide; + WideLowerBound = nullptr; + WideUpperBound = nullptr; + } + + void setWidePointer(Expr *Lower, Expr *Upper) { + WideLowerBound = Lower; + WideUpperBound = Upper; + WidePointerExpr = nullptr; + } + + void setAccessStartIndex(Expr *StartIndex) { + AccessStartIndex = StartIndex; + AccessLowerBound = nullptr; + } + + void setAccessLowerBound(Expr *Lower) { + AccessLowerBound = Lower; + AccessStartIndex = nullptr; + } + + void setAccessUpperBound(Expr *Upper) { + AccessUpperBound = Upper; + AccessCount = nullptr; + CountInBytes = 0; + OrNull = 0; + } + + void setAccessCount(Expr *Count, bool IsByteCount, bool IsOrNull) { + AccessCount = Count; + CountInBytes = IsByteCount; + OrNull = IsOrNull; + AccessUpperBound = nullptr; + } + + /// Create the bounds check expression. The value the bounds check evaluates + /// to when it succeeds will be GuardedValue. + ExprResult Build(Sema &S, Expr *GuardedValue); + + /// Create the bounds check expression to emit inequality `<=` checks with elements of \p Bounds in the order. + /// The function materializes elements of \p Bounds when they are reused and add the materialized values to + /// \p OVEs. + static ExprResult BuildLEChecks(Sema &S, SourceLocation Loc, + ArrayRef Bounds, + SmallVectorImpl &OVEs); + + /// Create the bounds check expression that verifies that the flexible array + /// member does not exceed the bounds of the enclosing pointer. + static ExprResult + CheckFlexibleArrayMemberSize(Sema &S, SourceLocation Loc, BoundsCheckKind BCK, + Expr *FAMPtr, CopyExpr *DeclReplacer = nullptr); + static ExprResult + CheckFlexibleArrayMemberSizeWithOVEs(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, + CopyExpr *DeclReplacer = nullptr); + +private: + static ExprResult + CheckFlexibleArrayMemberSizeImpl(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, + Expr *GuardedValue, CopyExpr *DeclReplacer); + + ExprResult BuildImplicitPointerArith(Sema &S, BinaryOperatorKind Opc, Expr *LHS, Expr *RHS); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct SkipBodyInfo { SkipBodyInfo() = default; bool ShouldSkip = false; @@ -792,7 +933,9 @@ class Sema final : public SemaBase { ExprResult ImpCastExprToType( Expr *E, QualType Type, CastKind CK, ExprValueKind VK = VK_PRValue, const CXXCastPath *BasePath = nullptr, - CheckedConversionKind CCK = CheckedConversionKind::Implicit); + CheckedConversionKind CCK = CheckedConversionKind::Implicit, + // TO_UPSTREAM(BoundsSafety) + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. @@ -988,6 +1131,12 @@ class Sema final : public SemaBase { std::function ParseTypeFromStringCallback; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Callback to the parser to parse a type expressed as a string. + std::function + ParseBoundsAttributeArgFromStringCallback; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// VAListTagName - The declaration name corresponding to __va_list_tag. /// This is used as part of a hack to omit that class from ADL results. DeclarationName VAListTagName; @@ -1408,6 +1557,14 @@ class Sema final : public SemaBase { /// Source location for newly created implicit MSInheritanceAttrs SourceLocation ImplicitMSInheritanceAttrLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Default BoundsSafety pointer attributes for ABI-visible pointers. Applied to + /// ABI-visible pointers which do not have explicit BoundsSafety attributes. Can + /// be controlled with \#pragma clang abi_ptr_attr set(*ATTR*). Defaults to + /// __single in user code and __unsafe_indexable in system headers. + BoundsSafetyPointerAttributes CurPointerAbi; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// pragma clang section kind enum PragmaClangSectionKind { PCSK_Invalid = 0, @@ -2062,9 +2219,86 @@ class Sema final : public SemaBase { ExprResult Operand, SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) ON */ ExprResult BuildBuiltinBitCastExpr(SourceLocation KWLoc, TypeSourceInfo *TSI, Expr *Operand, SourceLocation RParenLoc); + ExprResult ActOnForgeBidiIndexable(SourceLocation KWLoc, Expr *Addr, + Expr *Size, SourceLocation RParenLoc); + + ExprResult ActOnForgeSingle(SourceLocation KWLoc, Expr *Addr, + SourceLocation RParenLoc); + + ExprResult ActOnForgeTerminatedBy(SourceLocation KWLoc, Expr *Addr, + Expr *Terminator, SourceLocation RParenLoc); + + ExprResult ActOnBoundsSafetyCall(ExprResult Call); + + ExprResult BuildForgePtrExpr(SourceLocation KWLoc, SourceLocation RParenLoc, + QualType ResultType, Expr *Addr, + Expr *Size = nullptr, Expr *Term = nullptr); + + /// Build a bounds check based on individual components. Each value of + /// Bounds is sequentially compared to be <= the next one. + ExprResult BuildBoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs); + + ExprResult BuildPredefinedBoundsCheckExpr(Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs); + + /// Build an expression that resolves to the lower bound of the pointer + /// represented by SubExpr. + ExprResult BuildLowerBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetLowerBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + /// Build an expression that resolves to the upper bound of the pointer + /// represented by SubExpr. + ExprResult BuildUpperBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetUpperBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildMaterializeSequenceExpr(Expr *WrappedExpr, + ArrayRef Values); + + ExprResult BuildTerminatedByToIndexableExpr(Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildTerminatedByFromIndexableExpr(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByFromIndexable(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Checks that reinterpret casts don't have undefined behavior. void CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType, bool IsDereference, SourceRange Range); @@ -2605,6 +2839,9 @@ class Sema final : public SemaBase { /// Adds an expression to the set of gathered misaligned members. void AddPotentialMisalignedMembers(Expr *E, RecordDecl *RD, ValueDecl *MD, CharUnits Alignment); + + // TO_UPSTREAM(BoundsSafety) + llvm::SmallPtrSet BoundsSafetyDeclsWithFixIts; ///@} // @@ -4170,6 +4407,13 @@ class Sema final : public SemaBase { void deduceOpenCLAddressSpace(ValueDecl *decl); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void deduceBoundsSafetyPointerTypes(ValueDecl *decl); + void deduceBoundsSafetyFunctionTypes(FunctionDecl *decl); + + QualType deduceCastPointerAttributes(QualType OrigTy, QualType OpTy); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Adjust the \c DeclContext for a function or variable that might be a /// function-local external declaration. static bool adjustContextForLocalExternDecl(DeclContext *&DC); @@ -4425,12 +4669,6 @@ class Sema final : public SemaBase { StringRef &Str, SourceLocation *ArgLocation = nullptr); - /// Determine if type T is a valid subject for a nonnull and similar - /// attributes. By default, we look through references (the behavior used by - /// nonnull), but if the second parameter is true, then we treat a reference - /// type as valid. - bool isValidPointerAttrType(QualType T, bool RefOkay = false); - /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular /// declaration. void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, @@ -6708,6 +6946,11 @@ class Sema final : public SemaBase { bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); + /* TO_UPSTREAM(BoundsSafety) ON*/ + ExprResult InstantiateDeclRefField(Expr *BaseExpr, bool IsArrow, + Expr *FieldRef); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// BuildQualifiedDeclarationNameExpr - Build a C++ qualified /// declaration name, generally during template instantiation. /// There's a large number of things which don't need to be done along @@ -7453,18 +7696,30 @@ class Sema final : public SemaBase { // DefaultFunctionArrayConversion - converts functions and arrays // to their respective pointers (C99 6.3.2.1). - ExprResult DefaultFunctionArrayConversion(Expr *E, bool Diagnose = true); + ExprResult DefaultFunctionArrayConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ // DefaultFunctionArrayLvalueConversion - converts functions and // arrays to their respective pointers and performs the // lvalue-to-rvalue conversion. - ExprResult DefaultFunctionArrayLvalueConversion(Expr *E, - bool Diagnose = true); + ExprResult DefaultFunctionArrayLvalueConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) ON */ // DefaultLvalueConversion - performs lvalue-to-rvalue conversion on // the operand. This function is a no-op if the operand has a function type // or an array type. - ExprResult DefaultLvalueConversion(Expr *E); + ExprResult + DefaultLvalueConversion(Expr *E, + // TO_UPSTREAM(BoundsSafety) + bool DisableFlexibleArrayPromotion = false); // DefaultArgumentPromotion (C99 6.5.2.2p6). Used for function calls that // do not have a prototype. Integer promotions are performed on each @@ -7534,6 +7789,66 @@ class Sema final : public SemaBase { /// accept as an extension. IntToPointer, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleIntToSafePointer - The assignment converts an int to a + /// -fbounds-safety safe pointer, which is not permitted by -fbounds-safety. + IncompatibleIntToSafePointer, + + /// IncompatibleUnsafeToSafePointer - The assignment converts an unsafe + /// pointer to a safe pointer (single or counted_by), which -fbounds-safety + /// doesn't allow. + IncompatibleUnsafeToSafePointer, + + /// IncompatibleUnsafeToIndexablePointer - The assignment converts an unsafe + /// pointer to an indexable pointer (indexable or bidi_indexable), + /// which -fbounds-safety doesn't allow and may not know how to create an upper + /// bound for. + IncompatibleUnsafeToIndexablePointer, + + // FIXME: Turn it into an error (rdar://85587619) + /// IncompleteSingleToIndexablePointer - The assignment converts an single + /// pointer with opaque element type to an indexable pointer, for which + /// -fbounds-safety + /// doesn't know how to create an upper bound. + IncompleteSingleToIndexablePointer, + + // CompatibleSingleToIndexablePointer - The assignment converts a single + // pointer to an explicitly indexable pointer. This is allowed but is likely + // a mistake. + CompatibleSingleToExplicitIndexablePointer, + + /// IncompatibleStringLiteralToValueTerminatedPointer - The assignment of a + /// string literal to value-terminated pointer with a terminator different + /// than NUL. + IncompatibleStringLiteralToValueTerminatedPointer, + + /// IncompatibleValueTerminatedTerminators - The assignment is between + /// value terminated pointers with incompatible terminators. + IncompatibleValueTerminatedTerminators, + + /// IncompatibleValueTerminatedToNonValueTerminatedPointer - The assignment + /// of a value-terminated pointer to a non-value-terminated pointer. + /// BoundsSafety doesn't allow it, __terminated_by_to_indexable() should be + /// used instead. + IncompatibleValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNonValueTerminatedToValueTerminatedPointer - The assignment + /// of a non-value-terminated pointer to a value-terminated pointer. + /// BoundsSafety doesn't allow it, __unsafe_terminated_by_from_wide() should + /// be used instead. + IncompatibleNonValueTerminatedToValueTerminatedPointer, + + /// IncompatibleNestedValueTerminatedToNonValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the source is value-terminated type, but the destination isn't. + IncompatibleNestedValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNestedNonValueTerminatedToValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the destination is value-terminated type, but the source isn't. + IncompatibleNestedNonValueTerminatedToValueTerminatedPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// FunctionVoidPointer - The assignment is between a function pointer and /// void*, which the standard doesn't allow, but we accept as an extension. FunctionVoidPointer, @@ -7580,6 +7895,18 @@ class Sema final : public SemaBase { /// extension. IncompatibleNestedPointerQualifiers, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleNestedBoundsSafetyPointerAttributes - The assignment is + /// between two nested pointer types with different -fbounds-safety pointer + /// attributes. + IncompatibleNestedBoundsSafetyPointerAttributes, + + /// IncompatibleBoundsSafetyFunctionPointer - The assignment is + /// between two function pointer types with incompatible -fbounds-safety pointer + /// attributes. + IncompatibleBoundsSafetyFunctionPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// IncompatibleVectors - The assignment is between two vector types that /// have the same size, which we accept as an extension. IncompatibleVectors, @@ -7606,13 +7933,78 @@ class Sema final : public SemaBase { Incompatible }; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// @brief Check whether it's ABI safe to convert between two types that would + /// otherwise be forbidden by -fbounds-safety. Returns false if \c ConvTy ... + /// - is not an incompatibility + /// - is not a -fbounds-safety specific incompatibility + /// - represents an incompatible conversion between pointers of different + /// widths + /// Otherwise returns true. + bool isCompatibleBoundsUnsafeAssignment(AssignConvertType ConvTy) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If using this function to make the actual decision of ignoring the type + /// clash then \c isCompatibleBoundsUnsafeAssignment should be called first. + /// If using this function after type checking (e.g. to determine whether to + /// skip bounds checks) the only remaining cases where it returns true are + /// then ones that have previously passed \c + /// isCompatibleBoundsUnsafeAssignment during type checking, so that need not + /// be called again. + /// @param DestTy Type declared at LHS of variable assignment or function to + /// be called + /// @param SourceValue RHS of variable assignment, or function argument + /// @param AssignmentLoc Location of the actual assignment or function call + /// @return true when the -fbounds-safety rules can be skipped + bool allowBoundsUnsafePointerAssignment(const QualType DestTy, + const Expr *SourceValue, + SourceLocation AssignmentLoc) const; + + /// @brief Helper function wrapping allowBoundsUnsafePointerAssignment. + /// Only for function calls, not for assignments or returns. + bool allowBoundsUnsafeFunctionArg(const CallExpr *CallE, + unsigned ParamIdx) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If determining whether a pointer assignment should be allowed, use \c + /// allowBoundsUnsafePointerAssignment (which will call this function, in + /// addition to other checks). This function can be called directly for + /// determining whether unsafe count parameter assignments should be allowed. + /// @param AssignmentLoc Location of an expression assigning a new value to + /// a bounds safe variable. + bool allowBoundsUnsafeAssignment(SourceLocation AssignmentLoc) const; + + void TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr( + const ValueDecl *Assignee, Expr *SrcExpr, QualType DstType, AssignmentAction Action); + void + TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningBidiIndexableExprToNullTerminated(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningNullTerminatedToBidiIndexableExpr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningSinglePtrToNullTerminated(Expr *SrcExpr, QualType SrcType, + QualType DstType); + void TryFixAssigningConstArrayToNullTerminated(const Expr *SrcExpr, + QualType SrcType, + QualType DstType); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// DiagnoseAssignmentResult - Emit a diagnostic, if required, for the /// assignment conversion type specified by ConvTy. This returns true if the /// conversion was invalid or false if the conversion was accepted. bool DiagnoseAssignmentResult(AssignConvertType ConvTy, SourceLocation Loc, QualType DstType, QualType SrcType, Expr *SrcExpr, AssignmentAction Action, - bool *Complained = nullptr); + bool *Complained = nullptr, + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *Assignee = nullptr); /// CheckAssignmentConstraints - Perform type checking for assignment, /// argument passing, variable initialization, and function return values. @@ -7651,6 +8043,25 @@ class Sema final : public SemaBase { AssignConvertType CheckTransparentUnionArgumentConstraints(QualType ArgType, ExprResult &RHS); + /* TO_UPSTREAM(BoundsSafety) ON*/ + bool CheckDynamicBoundVariableEscape(QualType LHSType, Expr *RHSExp); + + using DependentValuesMap = + llvm::DenseMap>; + bool CheckDynamicCountSizeForAssignment(QualType LHSTy, Expr *RHSExpr, + AssignmentAction Action, + SourceLocation Loc, + const Twine &Designator, + DependentValuesMap &DependentValues, + Expr *LHSMemberBase = nullptr); + + void DiagnoseSingleToWideLosingBounds(QualType LHSType, QualType RHSType, + const Expr *RHSExp); + bool DiagnoseDynamicCountVarZeroInit(VarDecl *VD); + Sema::AssignConvertType + CheckValueTerminatedAssignmentConstraints(QualType LHSType, Expr *RHSExpr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). @@ -7675,6 +8086,9 @@ class Sema final : public SemaBase { BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr); QualType CheckSubtractionOperands( // C99 6.5.6 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, + /*TO_UPSTREAM(BoundsSafety) ON*/ + BinaryOperatorKind Opc, + /*TO_UPSTREAM(BoundsSafety) OFF*/ QualType *CompLHSTy = nullptr); QualType CheckShiftOperands( // C99 6.5.7 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, @@ -12093,6 +12507,10 @@ class Sema final : public SemaBase { /// \param A the argument type. bool isSameOrCompatibleFunctionType(QualType Param, QualType Arg); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void CheckValueTerminatedUninitialized(const VarDecl *VD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Allocate a TemplateArgumentLoc where all locations have /// been initialized to the given location. /// @@ -14521,8 +14939,8 @@ class Sema final : public SemaBase { /// /// \returns A suitable pointer type, if there are no /// errors. Otherwise, returns a NULL type. - QualType BuildPointerType(QualType T, SourceLocation Loc, - DeclarationName Entity); + QualType BuildPointerType(QualType T, BoundsSafetyPointerAttributes A, + SourceLocation Loc, DeclarationName Entity); /// Build a reference type. /// @@ -14825,6 +15243,54 @@ class Sema final : public SemaBase { QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Lifetime check for variables with function scope. + enum class LifetimeCheckKind { + // Do not check. + None, + + // Dependent variable must be a non-static local variable. + NonStaticLocal, + + // Dependent variable must be a static local variable. + StaticLocal, + + // Dependent variable must be an extern variable including extern local + // variable declaration. + Extern, + + // Dependent variable must be a private extern variable. + PrivateExtern, + + // Dependent variable must be a static global variable. + StaticGlobal, + + // Dependent variable must be a global variable definition. + // Technically, this doesn't tell the lifetime difference compared to + // Extern, + // but we keep this kind to track globals defined in this translation unit. + GlobalDefinition, + }; + + static LifetimeCheckKind getLifetimeCheckKind(const VarDecl *VD); + + /// Attach \c DependerDeclsAttr to declarations referred to by \c counted_by + /// or \c sized_by attributes. This doesn't apply to \c ended_by because it + /// adds a type sugar (i.e., \c DynamicRangePointerType) instead for its + /// dependent declaration. + void AttachDependerDeclsAttr(ValueDecl *NewDepender, + const CountAttributedType *NewDependerCountTy, + unsigned Level); + + QualType BuildCountAttributedType(QualType PointerTy, Expr *CountExpr, + bool CountInBytes = false, + bool OrNull = false, + bool ScopeCheck = false); + + QualType BuildDynamicRangePointerType(QualType PointerTy, Expr *StartPtr, + Expr *EndPtr, bool ScopeCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Ensure that the type T is a literal type. /// /// This routine checks whether the type @p T is a literal type. If @p T is an @@ -15038,6 +15504,147 @@ class Sema final : public SemaBase { llvm::SmallVectorImpl &Decls, bool CountInBytes, bool OrNull); + /* TO_UPSTREAM(BoundsSafety) ON */ + void applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, + AttributeCommonInfo::Kind Kind, + Expr *AttrArg, SourceLocation Loc, + SourceRange Range, StringRef DiagName, + bool OriginatesInAPINotes = false); + + /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or + /// `__counted_by_or_null` pointer type \param LHSTy. + /// + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being assigned from. + /// \param Action The type assignment being performed + /// \param Loc The SourceLocation to use for error diagnostics + /// \param ComputeAssignee If provided this function will be called before + /// emitting a diagnostic. The function should return the name of + /// entity being assigned to or an empty string if this cannot be + /// determined. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckAssignmentToCountAttrPtr( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, + std::function ComputeAssignee = nullptr); + + /// Perform Checks for assigning to a `__counted_by` or + /// `__counted_by_or_null` pointer type \param LHSTy where the pointee type + /// is incomplete which is invalid. + /// + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being assigned from. + /// \param Action The type assignment being performed + /// \param Loc The SourceLocation to use for error diagnostics + /// \param ComputeAssignee If provided this function will be called before + /// emitting a diagnostic. The function should return the name of + /// entity being assigned to or an empty string if this cannot be + /// determined. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, std::function ComputeAssignee); + + /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety + /// pointer. + /// + /// \param Entity The entity being initialized + /// \param Kind The kind of initialization being performed + /// \param Action The type assignment being performed + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being used for initialization. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity, + const InitializationKind &Kind, + Sema::AssignmentAction Action, + QualType LHSType, Expr *RHSExpr); + + /// Perform Bounds Safety semantic checks on function parameters on a function + /// definition. This only performs checks that can be made by looking at + /// \param PVD in isolation (i.e. not looking at other parameters in the + /// function definition). + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckParamForFunctionDef(const ParmVarDecl *PVD); + + /// Perform Bounds Safety semantic check for CallExpr \param Call. + /// + /// \param FDecl For direct calls this should be the Function being called + /// by \param Call. This can be null. + /// \param Call The CallExpr to be checked. This cannot be null. + /// \param ProtoType For indirect calls this should be the FunctionProtoType + /// used to type check the call. This can be null. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckResolvedCall(FunctionDecl *FDecl, CallExpr *Call, + const FunctionProtoType *ProtoType); + + /// Perform Bounds Safety semantic checks on a function definition's return + /// type. + /// + /// \param FD The FunctionDecl whose return type will be checked. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD); + + /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by + /// or counted_by_or_null pointers in \param E. + /// + /// \param E the expression to check + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckUseOfCountAttrPtr(Expr *E); + + /// Perform Bounds Safety semantic checks on variable declaration \param VD. + /// + /// \param VD The VarDecl to check + /// \param CheckTentativeDefinitions If false and \param VD is a tentative + /// definition then checking is skipping and + /// the function returns true. If true then + /// checking of tentative definitions will + /// not be skipped. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckVarDecl(const VarDecl *VD, + bool CheckTentativeDefinitions); + + // TODO: This can be moved back into SemaExpr.cpp as a static function once + // support for -fno-bounds-safety-bringup-missing-checks=indirect_count_update + // is removed (rdar://135833598). + bool BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + const CountAttributedType *CATTy, Expr *Operand, + std::variant OpInfo); + + + /// Used to record or retrieve if a VarDecl/FieldDecl has a BoundsSafety FixIt + /// emitted on it. The primary use case for this is preventing emitting + /// multiple FixIts on the same VarDecl/FieldDecl which can result in invalid + /// code. + /// + /// Note: This is not linked to the DiagnosticEngine in anyway. So callers + /// are required to manually maintain this information. + /// + /// \param DD - the VarDecl/FieldDecl to record or retrieve information about. + /// \param Set - If `Set` is `false` then this is a retrieval operation. The + /// method will return `true` if the `DD` already had a FixIt emitted and + /// `false` otherwise.. If `Set` is `true` then this is method records that + /// the `DD` had a FixIt emitted against it. The returned value will + /// always be `true` in this case. + /// + /// \returns `true` iff it was recorded that a FixIt was emitted for the + /// VarDecl `VD`. + /// + bool BoundsSafetyFixItWasEmittedFor(const DeclaratorDecl *DD, + bool Set = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + ///@} // diff --git a/clang/include/clang/Sema/SemaFixItUtils.h b/clang/include/clang/Sema/SemaFixItUtils.h index df9bc42976943..da1ecb901289e 100644 --- a/clang/include/clang/Sema/SemaFixItUtils.h +++ b/clang/include/clang/Sema/SemaFixItUtils.h @@ -86,5 +86,80 @@ struct ConversionFixItGenerator { } }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class BoundsSafetyFixItUtils { +public: + /// Try to find the SourceLocation where a bounds-safety attribute could + /// be inserted on a pointer. Note this method does not check if there is an + /// attribute already present. Clients should handle this themselves. + /// + /// + /// \param TL - TypeLoc that the attribute could be added to + /// \param S - Sema instance + /// + /// \return a tuple of the SourceLocation where insertion could be performed + /// and a boolean that is true iff a space should be inserted after the + /// inserted attribute. If the returned SourceLocation is invalid no insertion + /// point could be found. + static std::tuple + FindPointerAttrInsertPoint(const TypeLoc TL, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the variable declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const VarDecl *VD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the field declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Field Declaration to suggest FixIt for. This field + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the field declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const FieldDecl *FD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration and all of its previous + /// declarations. Note only global variables may have previous declarations. + /// Note this method does not check for existing attributes. + /// Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// \param FixIts - A SmallVector of (FixIts hint, VarDecl) tuples. Every + /// valid FixHit that can be created will be added to this SmallVector. Each + /// FixIt hint adds the supplied Attribute to the type specifier on each of + /// the variable declarations. + static void CreateAnnotateAllPointerDeclsFixIts( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S, + llvm::SmallVectorImpl> + &FixIts); + +private: + static FixItHint CreateAnnotateVarDeclOrFieldDeclFixIt( + const DeclaratorDecl *VD, const llvm::StringRef Attribute, Sema &S); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + } // endof namespace clang #endif diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index a6c1c20814de4..2c1b17f4a70ae 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -44,7 +44,7 @@ namespace serialization { /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. -const unsigned VERSION_MAJOR = 33; +const unsigned VERSION_MAJOR = 34; /// AST file minor version number supported by this version of /// Clang. @@ -350,9 +350,8 @@ enum ControlRecordTypes { /// and information about the compiler used to build this AST file. METADATA = 1, - /// Record code for the list of other AST files imported by - /// this AST file. - IMPORTS, + /// Record code for another AST file imported by this AST file. + IMPORT, /// Record code for the original file that was used to /// generate the AST file, including both its file ID and its @@ -1896,6 +1895,19 @@ enum StmtCode { // OpenCL EXPR_ASTYPE, // AsTypeExpr + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety + EXPR_ASSUMPTION, // AssumptionExpr + EXPR_BOUNDS_SAFETY_POINTER_PROMOTION, // BoundsSafetyPointerPromotionExpr + EXPR_FORGE_PTR, // ForgePtrExpr + EXPR_GET_BOUND, // GetBoundExpr + EXPR_PREDEFINED_BOUNDS_CHECK, // PredefinedBoundsCheckExpr + EXPR_BOUNDS_CHECK, // BoundsCheckExpr + EXPR_MATERIALIZE_SEQUENCE, // MaterializeSequenceExpr + EXPR_TERMINATED_BY_TO_INDEXABLE, // TerminatedByToIndexableExpr + EXPR_TERMINATED_BY_FROM_INDEXABLE, // TerminatedByFromIndexableExpr + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Microsoft EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 16b63680ec6b6..1dcfa22c437b8 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -2361,11 +2361,8 @@ class ASTReader // Read a string static std::string ReadString(const RecordDataImpl &Record, unsigned &Idx); - - // Skip a string - static void SkipString(const RecordData &Record, unsigned &Idx) { - Idx += Record[Idx] + 1; - } + static StringRef ReadStringBlob(const RecordDataImpl &Record, unsigned &Idx, + StringRef &Blob); // Read a path std::string ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx); @@ -2373,11 +2370,8 @@ class ASTReader // Read a path std::string ReadPath(StringRef BaseDirectory, const RecordData &Record, unsigned &Idx); - - // Skip a path - static void SkipPath(const RecordData &Record, unsigned &Idx) { - SkipString(Record, Idx); - } + std::string ReadPathBlob(StringRef BaseDirectory, const RecordData &Record, + unsigned &Idx, StringRef &Blob); /// Read a version tuple. static VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx); diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index 2561418b78ca7..b833b6dae357c 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -171,6 +171,12 @@ class ASTRecordReader return Qualifiers::fromOpaqueValue(readInt()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + return BoundsSafetyPointerAttributes::fromOpaqueValue(readUInt32()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Read a type from the current position in the record. QualType readType() { return Reader->readType(*F, Record, Idx); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index db27904941098..d41da111ebe72 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -758,6 +758,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a string to the given record. void AddString(StringRef Str, RecordDataImpl &Record); + void AddStringBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob); /// Convert a path from this build process into one that is appropriate /// for emission in the module file. @@ -765,6 +767,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a path to the given record. void AddPath(StringRef Path, RecordDataImpl &Record); + void AddPathBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob); /// Emit the current record with the given path as a blob. void EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, @@ -904,6 +908,10 @@ class ASTWriter : public ASTDeserializationListener, void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + /* TO_UPSTREAM(BoundsSafety) ON*/ + void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) override; + /* TO_UPSTREAM(BoundsSafety) OFF*/ void EnteringModulePurview() override; void AddedManglingNumber(const Decl *D, unsigned) override; void AddedStaticLocalNumbers(const Decl *D, unsigned) override; diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index 82b053d4caca6..de16fe5b81259 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -66,6 +66,10 @@ TYPE_BIT_CODE(Using, USING, 54) TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55) TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56) TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57) -TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58) +/* TO_UPSTREAM(BoundsSafety) ON */ +TYPE_BIT_CODE(DynamicRangePointer, DYNAMIC_END_POINTER, 58) +TYPE_BIT_CODE(ValueTerminated, VALUE_TERMINATED, 59) +/* TO_UPSTREAM(BoundsSafety) OFF */ +TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 60) #undef TYPE_BIT_CODE diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 939235179c363..189240f734a3c 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 34; // SwiftReturnOwnership +const uint16_t VERSION_MINOR = 35; // BoundsSafety const uint8_t kSwiftConforms = 1; const uint8_t kSwiftDoesNotConform = 2; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 11d2c7a4c8df6..c1e2aec7ba7f1 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -322,6 +322,31 @@ class FieldTableInfo } }; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Read serialized BoundsSafetyInfo. +void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) { + uint8_t Payload = endian::readNext(Data); + + if (Payload & 0x01) { + uint8_t Level = (Payload >> 1) & 0x7; + Info.setLevelAudited(Level); + } + Payload >>= 4; + + if (Payload & 0x01) { + uint8_t Kind = (Payload >> 1) & 0x7; + assert(Kind >= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::CountedBy); + assert(Kind <= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::EndedBy); + Info.setKindAudited((BoundsSafetyInfo::BoundsSafetyKind)Kind); + } + + uint16_t ExternalBoundsLen = + endian::readNext(Data); + Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen); + Data += ExternalBoundsLen; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Read serialized ParamInfo. void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) { ReadVariableInfo(Data, Info); @@ -338,7 +363,13 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) { if (Payload & 0x01) Info.setNoEscape(Payload & 0x02); Payload >>= 2; - assert(Payload == 0 && "Bad API notes"); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Payload & 0x01) { + BoundsSafetyInfo BSI; + ReadBoundsSafetyInfo(Data, BSI); + Info.BoundsSafety = BSI; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ } /// Read serialized FunctionInfo. diff --git a/clang/lib/APINotes/APINotesTypes.cpp b/clang/lib/APINotes/APINotesTypes.cpp index f726faa832bcc..df7b67a21b66e 100644 --- a/clang/lib/APINotes/APINotesTypes.cpp +++ b/clang/lib/APINotes/APINotesTypes.cpp @@ -61,6 +61,34 @@ LLVM_DUMP_METHOD void ObjCPropertyInfo::dump(llvm::raw_ostream &OS) const { OS << '\n'; } +LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const { + if (KindAudited) { + assert((BoundsSafetyKind)Kind >= BoundsSafetyKind::CountedBy); + assert((BoundsSafetyKind)Kind <= BoundsSafetyKind::EndedBy); + switch ((BoundsSafetyKind)Kind) { + case BoundsSafetyKind::CountedBy: + OS << "[counted_by] "; + break; + case BoundsSafetyKind::CountedByOrNull: + OS << "[counted_by_or_null] "; + break; + case BoundsSafetyKind::SizedBy: + OS << "[sized_by] "; + break; + case BoundsSafetyKind::SizedByOrNull: + OS << "[sized_by_or_null] "; + break; + case BoundsSafetyKind::EndedBy: + OS << "[ended_by] "; + break; + } + } + if (LevelAudited) + OS << "Level: " << Level << " "; + OS << "ExternalBounds: " + << (ExternalBounds.empty() ? "" : ExternalBounds) << '\n'; +} + LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const { static_cast(*this).dump(OS); if (NoEscapeSpecified) @@ -69,6 +97,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const { OS << (Lifetimebound ? "[Lifetimebound] " : ""); OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' '; OS << '\n'; + if (BoundsSafety) + BoundsSafety->dump(OS); } LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const { diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index cbe6bccb80860..b7f3fa2a467a1 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1074,15 +1074,48 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock( } } +/* TO_UPSTREAM(BoundsSafety) ON */ namespace { +void emitBoundsSafetyInfo(raw_ostream &OS, const BoundsSafetyInfo &BSI) { + llvm::support::endian::Writer writer(OS, llvm::endianness::little); + uint8_t flags = 0; + if (auto kind = BSI.getKind()) { + flags |= 0x01; // 1 bit + flags |= (uint8_t)*kind << 1; // 3 bits + } + flags <<= 4; + if (auto level = BSI.getLevel()) { + flags |= 0x01; // 1 bit + flags |= *level << 1; // 3 bits + } + + writer.write(flags); + writer.write(BSI.ExternalBounds.size()); + writer.write( + ArrayRef{BSI.ExternalBounds.data(), BSI.ExternalBounds.size()}); +} + +unsigned getBoundsSafetyInfoSize(const BoundsSafetyInfo &BSI) { + return 1 + sizeof(uint16_t) + BSI.ExternalBounds.size(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + unsigned getParamInfoSize(const ParamInfo &PI) { - return getVariableInfoSize(PI) + 1; + unsigned BSISize = 0; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (auto BSI = PI.BoundsSafety) + BSISize = getBoundsSafetyInfoSize(*BSI); + /* TO_UPSTREAM(BoundsSafety) OFF */ + return getVariableInfoSize(PI) + 1 + BSISize; } void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) { emitVariableInfo(OS, PI); uint8_t flags = 0; + if (PI.BoundsSafety) + flags |= 0x01; + flags <<= 2; if (auto noescape = PI.isNoEscape()) { flags |= 0x01; if (*noescape) @@ -1100,6 +1133,10 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(flags); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (auto BSI = PI.BoundsSafety) + emitBoundsSafetyInfo(OS, *PI.BoundsSafety); + /* TO_UPSTREAM(BoundsSafety) OFF */ } /// Retrieve the serialized size of the given FunctionInfo, for use in on-disk diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index d623b9042ee1a..86939431adcd5 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -150,6 +150,33 @@ enum class APIAvailability { }; } // namespace +/* TO_UPSTREAM(BoundsSafety) ON */ +namespace { +struct BoundsSafety { + BoundsSafetyInfo::BoundsSafetyKind Kind; + unsigned Level = 0; + StringRef BoundsExpr = ""; +}; +} // namespace + +namespace llvm { +namespace yaml { +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, BoundsSafetyInfo::BoundsSafetyKind &AA) { + IO.enumCase(AA, "counted_by", + BoundsSafetyInfo::BoundsSafetyKind::CountedBy); + IO.enumCase(AA, "counted_by_or_null", + BoundsSafetyInfo::BoundsSafetyKind::CountedByOrNull); + IO.enumCase(AA, "sized_by", BoundsSafetyInfo::BoundsSafetyKind::SizedBy); + IO.enumCase(AA, "sized_by_or_null", + BoundsSafetyInfo::BoundsSafetyKind::SizedByOrNull); + IO.enumCase(AA, "ended_by", BoundsSafetyInfo::BoundsSafetyKind::EndedBy); + } +}; +} // namespace yaml +} // namespace llvm +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { @@ -187,6 +214,9 @@ struct Param { std::optional Lifetimebound = false; std::optional Nullability; std::optional RetainCountConvention; + /* TO_UPSTREAM(BoundsSafety) ON */ + std::optional BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ StringRef Type; }; @@ -238,8 +268,21 @@ template <> struct MappingTraits { IO.mapOptional("NoEscape", P.NoEscape); IO.mapOptional("Lifetimebound", P.Lifetimebound); IO.mapOptional("Type", P.Type, StringRef("")); + /* TO_UPSTREAM(BoundsSafety) ON */ + IO.mapOptional("BoundsSafety", P.BoundsSafety); + /* TO_UPSTREAM(BoundsSafety) OFF */ } }; + +/* TO_UPSTREAM(BoundsSafety) ON */ +template <> struct MappingTraits { + static void mapping(IO &IO, BoundsSafety &BS) { + IO.mapRequired("Kind", BS.Kind); + IO.mapRequired("BoundedBy", BS.BoundsExpr); + IO.mapOptional("Level", BS.Level, 0); + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ } // namespace yaml } // namespace llvm @@ -862,6 +905,15 @@ class YAMLConverter { PI.setLifetimebound(P.Lifetimebound); PI.setType(std::string(P.Type)); PI.setRetainCountConvention(P.RetainCountConvention); + BoundsSafetyInfo BSI; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (P.BoundsSafety) { + BSI.setKindAudited(P.BoundsSafety->Kind); + BSI.setLevelAudited(P.BoundsSafety->Level); + BSI.ExternalBounds = P.BoundsSafety->BoundsExpr.str(); + } + PI.BoundsSafety = BSI; + /* TO_UPSTREAM(BoundsSafety) OFF */ if (static_cast(OutInfo.Params.size()) <= P.Position) OutInfo.Params.resize(P.Position + 1); if (P.Position == -1) diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index d8e33ff421c06..69bd2e8efb1f9 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -47,11 +47,19 @@ APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, QualType Type) { LValueBase Base; - Base.Ptr = LV; + Base.Ptr = DynamicAllocOrForgedPtrLValue(LV); Base.DynamicAllocType = Type.getAsOpaquePtr(); return Base; } +APValue::LValueBase APValue::LValueBase::getForgedPtr(ForgedPtrLValue LV, + QualType Type) { + LValueBase Base; + Base.Ptr = DynamicAllocOrForgedPtrLValue(LV); + Base.ForgedPtrAsArrayType = Type.getAsOpaquePtr(); + return Base; +} + APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, QualType TypeInfo) { LValueBase Base; @@ -61,6 +69,11 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, } QualType APValue::LValueBase::getType() const { + // BOUNDS-SAFETY: forged ptr's base is null, when it's constant but it can + // still have a valid type information. + if (is()) + return getForgedPtrAsArrayType(); + if (!*this) return QualType(); if (const ValueDecl *D = dyn_cast()) { // FIXME: It's unclear where we're supposed to take the type from, and @@ -106,12 +119,17 @@ QualType APValue::LValueBase::getType() const { } unsigned APValue::LValueBase::getCallIndex() const { - return (is() || is()) ? 0 - : Local.CallIndex; + return (is() || is() || + is()) + ? 0 + : Local.CallIndex; } unsigned APValue::LValueBase::getVersion() const { - return (is() || is()) ? 0 : Local.Version; + return (is() || is() || + is()) + ? 0 + : Local.Version; } QualType APValue::LValueBase::getTypeInfoType() const { @@ -124,9 +142,23 @@ QualType APValue::LValueBase::getDynamicAllocType() const { return QualType::getFromOpaquePtr(DynamicAllocType); } +QualType APValue::LValueBase::getForgedPtrAsArrayType() const { + assert(is() && "not a forged ptr lvalue"); + return QualType::getFromOpaquePtr(ForgedPtrAsArrayType); +} + +const ValueDecl *APValue::LValueBase::getValueDecl() const { + if (const ValueDecl *D = dyn_cast()) + return D; + if (ForgedPtrLValue LV = dyn_cast()) + return LV.getBaseValueDecl(); + return nullptr; +} + void APValue::LValueBase::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Ptr.getOpaqueValue()); - if (is() || is()) + if (is() || is() || + is()) return; ID.AddInteger(Local.CallIndex); ID.AddInteger(Local.Version); @@ -137,7 +169,8 @@ bool operator==(const APValue::LValueBase &LHS, const APValue::LValueBase &RHS) { if (LHS.Ptr != RHS.Ptr) return false; - if (LHS.is() || LHS.is()) + if (LHS.is() || LHS.is() || + LHS.is()) return true; return LHS.Local.CallIndex == RHS.Local.CallIndex && LHS.Local.Version == RHS.Local.Version; @@ -162,16 +195,6 @@ QualType APValue::LValuePathSerializationHelper::getType() { return QualType::getFromOpaquePtr(Ty); } -namespace { - struct LVBase { - APValue::LValueBase Base; - CharUnits Offset; - unsigned PathLength; - bool IsNullPtr : 1; - bool IsOnePastTheEnd : 1; - }; -} - void *APValue::LValueBase::getOpaqueValue() const { return Ptr.getOpaqueValue(); } @@ -181,6 +204,9 @@ bool APValue::LValueBase::isNull() const { } APValue::LValueBase::operator bool () const { + if (is()) { + return static_cast(get()); + } return static_cast(Ptr); } @@ -200,7 +226,8 @@ llvm::DenseMapInfo::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { - if (Base.is() || Base.is()) + if (Base.is() || Base.is() || + Base.is()) return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); @@ -219,6 +246,7 @@ bool llvm::DenseMapInfo::isEqual( } struct APValue::LV : LVBase { + static_assert(DataSize >= sizeof(LVBase) && "LVBase too big"); static const unsigned InlinePathSpace = (DataSize - sizeof(LVBase)) / sizeof(LValuePathEntry); @@ -348,6 +376,20 @@ APValue::APValue(const APValue &RHS) : Kind(None) { else setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath(), RHS.isNullPointer()); + if (RHS.isLValueForgeSingle()) { + assert(!isNullPointer() && !isLValueForgeBidi() && + !isLValueForgeTerminatedBy()); + setLValueForgedSingle(RHS.getLValueForgedOffset()); + } else if (RHS.isLValueForgeBidi()) { + assert(!isNullPointer() && !isLValueForgeSingle() && + !isLValueForgeTerminatedBy()); + setLValueForgedBidi(RHS.getLValueForgedSize(), + RHS.getLValueForgedOffset()); + } else if (RHS.isLValueForgeTerminatedBy()) { + assert(!isNullPointer() && !isLValueForgeSingle() && + !isLValueForgeBidi()); + setLValueForgedTerminatedBy(RHS.getLValueForgedOffset()); + } break; case Array: MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize()); @@ -606,6 +648,8 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const { for (LValuePathEntry E : getLValuePath()) E.Profile(ID); } + ID.AddInteger(getLValueForgedOffset().getQuantity()); + ID.AddInteger(getLValueForgedSize().getQuantity()); return; case MemberPointer: @@ -690,6 +734,34 @@ static bool TryPrintAsStringLiteral(raw_ostream &Out, return true; } +static void printForgedPtrLValue(raw_ostream &Out, const PrintingPolicy &Policy, + const ASTContext *Ctx, const APValue &V, + ForgedPtrLValue FP, QualType Ty) { + Out << "__unsafe_forge_" + << (V.isLValueForgeSingle() + ? "single(" + : (V.isLValueForgeTerminatedBy() ? "terminated_by(" + : "bidi_indexable(")); + QualType(Ty->getPointeeOrArrayElementType(), 0).print(Out, Policy); + Out << ", "; + + if (auto BaseVD = FP.getBaseValueDecl()) { + Out << *BaseVD << " + "; + } + // LValueOffset + ForgedOffset + Out << V.getUnwrappedLValueOffset().getQuantity(); + + if (V.isLValueForgeSingle()) { + Out << ")"; + } else if (V.isLValueForgeTerminatedBy()) { + Out << ", " << Ty->getAs()->getTerminatorValue(*Ctx) + << ")"; + } else { + assert(V.isLValueForgeBidi()); + Out << ", " << V.getLValueForgedSize().getQuantity() << ")"; + } +} + void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, QualType Ty) const { printPretty(Out, Ctx.getPrintingPolicy(), Ty, &Ctx); @@ -757,17 +829,17 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << (Policy.Nullptr ? "nullptr" : "0"); } else if (IsReference) { Out << "*(" << InnerTy.stream(Policy) << "*)" - << getLValueOffset().getQuantity(); + << getUnwrappedLValueOffset().getQuantity(); } else { Out << "(" << Ty.stream(Policy) << ")" - << getLValueOffset().getQuantity(); + << getUnwrappedLValueOffset().getQuantity(); } return; } if (!hasLValuePath()) { // No lvalue path: just print the offset. - CharUnits O = getLValueOffset(); + CharUnits O = getUnwrappedLValueOffset(); CharUnits S = Ctx ? Ctx->getTypeSizeInCharsIfKnown(InnerTy).value_or( CharUnits::Zero()) : CharUnits::Zero(); @@ -791,6 +863,8 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << "{*new " << Base.getDynamicAllocType().stream(Policy) << "#" << DA.getIndex() << "}"; + } else if (ForgedPtrLValue FP = Base.dyn_cast()) { + printForgedPtrLValue(Out, Policy, Ctx, *this, FP, Ty); } else { assert(Base.get() != nullptr && "Expecting non-null Expr"); @@ -819,6 +893,9 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, } else if (DynamicAllocLValue DA = Base.dyn_cast()) { Out << "{*new " << Base.getDynamicAllocType().stream(Policy) << "#" << DA.getIndex() << "}"; + } else if (ForgedPtrLValue FP = Base.dyn_cast()) { + printForgedPtrLValue(Out, Policy, Ctx, *this, FP, Ty); + return; } else { const Expr *E = Base.get(); assert(E != nullptr && "Expecting non-null Expr"); @@ -964,7 +1041,7 @@ bool APValue::toIntegralConstant(APSInt &Result, QualType SrcTy, } if (isLValue() && !getLValueBase()) { - Result = Ctx.MakeIntValue(getLValueOffset().getQuantity(), SrcTy); + Result = Ctx.MakeIntValue(getUnwrappedLValueOffset().getQuantity(), SrcTy); return true; } @@ -986,6 +1063,22 @@ CharUnits &APValue::getLValueOffset() { return ((LV *)(void *)&Data)->Offset; } +CharUnits &APValue::getLValueForgedSize() { + assert(isLValue() && "Invalid accessor"); + return ((LV *)(void *)&Data)->ForgedSize; +} + +CharUnits &APValue::getLValueForgedOffset() { + assert(isLValue() && "Invalid accessor"); + return ((LV *)(void *)&Data)->ForgedOffset; +} + +CharUnits APValue::getUnwrappedLValueOffset() const { + if (isLValueForge()) + return getLValueOffset() + getLValueForgedOffset(); + return getLValueOffset(); +} + bool APValue::hasLValuePath() const { assert(isLValue() && "Invalid accessor"); return ((const LV *)(const char *)&Data)->hasPath(); @@ -1012,6 +1105,27 @@ bool APValue::isNullPointer() const { return ((const LV *)(const char *)&Data)->IsNullPtr; } +bool APValue::isLValueForgeBidi() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeBidi; +} + +bool APValue::isLValueForgeSingle() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeSingle; +} + +bool APValue::isLValueForgeTerminatedBy() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeTerminatedBy; +} + +bool APValue::isLValueForge() const { + assert(isLValue() && "Invalid usage"); + return isLValueForgeBidi() || isLValueForgeSingle() || + isLValueForgeTerminatedBy(); +} + void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, bool IsNullPtr) { assert(isLValue() && "Invalid accessor"); @@ -1021,6 +1135,10 @@ void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, LVal.Offset = O; LVal.resizePath((unsigned)-1); LVal.IsNullPtr = IsNullPtr; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedSize = CharUnits::Zero(); } MutableArrayRef @@ -1033,9 +1151,47 @@ APValue::setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size, LVal.Offset = O; LVal.IsNullPtr = IsNullPtr; LVal.resizePath(Size); + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedSize = CharUnits::Zero(); return {LVal.getPath(), Size}; } +void APValue::setLValueForgedBidi(const CharUnits &Size, + const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + // For the purposes of constant evaluation, forged lvalues are never NULL, + // even if their address is 0. + LVal.IsNullPtr = false; + LVal.IsForgeBidi = true; + LVal.ForgedSize = Size; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedOffset = Offset; +} + +void APValue::setLValueForgedSingle(const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + LVal.IsNullPtr = false; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = true; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedOffset = Offset; +} + +void APValue::setLValueForgedTerminatedBy(const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + LVal.IsNullPtr = false; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = true; + LVal.ForgedOffset = Offset; +} + void APValue::setLValue(LValueBase B, const CharUnits &O, ArrayRef Path, bool IsOnePastTheEnd, bool IsNullPtr) { @@ -1187,6 +1343,12 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V, return LinkageInfo::internal(); if (MergeLV(getLVForDecl(MTE->getExtendingDecl(), computation))) break; + } else if (const auto FP = V.getLValueBase().dyn_cast()) { + if (const auto *VD = FP.getBaseValueDecl()) { + MergeLV(getLVForDecl(VD, computation)); + } + // Otherwise, it's null or absolute address, which is considered external. + break; } else { assert(V.getLValueBase().is() && "unexpected LValueBase kind"); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2ad44c6832898..d9bcec2e68d93 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -48,6 +48,7 @@ #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" #include "clang/AST/UnresolvedSet.h" #include "clang/AST/VTableBuilder.h" #include "clang/Basic/AddressSpaces.h" @@ -90,6 +91,7 @@ #include "llvm/Support/JSON.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/SipHash.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/AArch64TargetParser.h" @@ -884,9 +886,9 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()), TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), - DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()), - ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()), - SourceMgr(SM), LangOpts(LOpts), + DependentBitIntTypes(this_()), ValueTerminatedTypes(this_()), + SubstTemplateTemplateParmPacks(this_()), ArrayParameterTypes(this_()), + CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)), XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles, LangOpts.XRayNeverInstrumentFiles, @@ -2265,11 +2267,17 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; - case Type::Pointer: - AS = cast(T)->getPointeeType().getAddressSpace(); - Width = Target->getPointerWidth(AS); + case Type::Pointer: { + const auto *PT = cast(T); + AS = PT->getPointeeType().getAddressSpace(); + uint64_t RawPtrWidth = Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); + if (PT->getPointerAttributes().hasUpperBound()) + Width += RawPtrWidth; + if (PT->getPointerAttributes().hasLowerBound()) + Width += RawPtrWidth; break; + } case Type::MemberPointer: { const auto *MPT = cast(T); CXXABI::MemberPointerInfo MPI = ABI->getMemberPointerInfo(MPT); @@ -2392,6 +2400,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::CountAttributed: return getTypeInfo(cast(T)->desugar().getTypePtr()); + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + return getTypeInfo( + cast(T)->desugar().getTypePtr()); + + case Type::ValueTerminated: + return getTypeInfo(cast(T)->desugar().getTypePtr()); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Type::BTFTagAttributed: return getTypeInfo( cast(T)->getWrappedType().getTypePtr()); @@ -3459,11 +3476,497 @@ QualType ASTContext::getObjCGCQualType(QualType T, return getExtQualType(TypeNode, Quals); } +/* TO_UPSTREAM(BoundsSafety) ON */ +QualType ASTContext::getDynamicRangePointerType( + QualType PointerTy, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) const { + assert(PointerTy->isPointerType()); + + llvm::FoldingSetNodeID ID; + DynamicRangePointerType::Profile(ID, PointerTy, StartPtr, EndPtr, + StartPtrDecls.size(), EndPtrDecls.size()); + + void *InsertPos = nullptr; + DynamicRangePointerType *DRPTy = + DynamicRangePointerTypes.FindNodeOrInsertPos(ID, InsertPos); + if (DRPTy) + return QualType(DRPTy, 0); + + QualType CanonPTy = getCanonicalType(PointerTy); + size_t Size = + DynamicRangePointerType::totalSizeToAlloc( + EndPtrDecls.size() + StartPtrDecls.size()); + DRPTy = (DynamicRangePointerType *)Allocate(Size, TypeAlignment); + new (DRPTy) DynamicRangePointerType(PointerTy, CanonPTy, StartPtr, EndPtr, + StartPtrDecls, EndPtrDecls); + Types.push_back(DRPTy); + DynamicRangePointerTypes.InsertNode(DRPTy, InsertPos); + + return QualType(DRPTy, 0); +} + +QualType ASTContext::getValueTerminatedType(QualType T, + Expr *TerminatorExpr) const { + assert(T->isConstantArrayType() || T->isIncompleteArrayType() || + T->isPointerType()); + assert(!T->isValueTerminatedType()); + + SplitQualType Split = T.getSplitUnqualifiedType(); + + llvm::FoldingSetNodeID ID; + ValueTerminatedType::Profile(ID, *this, QualType(Split.Ty, 0), + TerminatorExpr); + + void *InsertPos = nullptr; + ValueTerminatedType *VTT = + ValueTerminatedTypes.FindNodeOrInsertPos(ID, InsertPos); + if (VTT) + return getQualifiedType(VTT, Split.Quals); + + QualType CanonTy = Split.Ty->getCanonicalTypeInternal(); + VTT = new (*this, TypeAlignment) + ValueTerminatedType(QualType(Split.Ty, 0), CanonTy, TerminatorExpr); + Types.push_back(VTT); + ValueTerminatedTypes.InsertNode(VTT, InsertPos); + + return getQualifiedType(VTT, Split.Quals); +} + +QualType ASTContext::getBoundsSafetyPointerType(QualType PointerTy, + BoundsSafetyPointerAttributes Attr) { + const auto *PTy = PointerTy->getAs(); + if (!PTy || PTy->getPointerAttributes() == Attr || + PointerTy->isBoundsAttributedType()) + return PointerTy; + + std::function + getBoundsSafetyPointerTypeRecurse = [&](QualType Ty) -> QualType { + if (const auto *AT = Ty->getAs()) { + auto ModifiedTy = + getBoundsSafetyPointerTypeRecurse(AT->getModifiedType()); + auto EquivalentTy = + getBoundsSafetyPointerTypeRecurse(AT->getEquivalentType()); + + auto QualsOnT = Ty.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + Ty = getAttributedType(AT->getAttrKind(), ModifiedTy, + EquivalentTy); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + Ty = QC.apply(*this, Ty); + } + return Ty; + } + assert(PointerTy->isPointerType()); + auto QualsOnT = PointerTy.getQualifiers(); + Ty = getPointerType(PointerTy->getPointeeType(), Attr); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + Ty = QC.apply(*this, Ty); + } + return Ty; + }; + + return getBoundsSafetyPointerTypeRecurse(PointerTy); +} + +/// Copies bounds Type node from \c SrcTy to \c DestTy if it does +/// not already have the corresponding Type. +static QualType assureMandatorySugarTypesRemain(ASTContext &Ctx, + QualType DestTy, + QualType SrcTy) { + if (auto VTT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getValueTerminatedType(DestTy, VTT->getTerminatorExpr()); + } + } else if (auto DCPT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getCountAttributedType(DestTy, DCPT->getCountExpr(), + DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } + } else if (auto DRPT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getDynamicRangePointerType( + DestTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } + } + + return DestTy; +} + +QualType ASTContext::mergeBoundsSafetyPointerTypes( + QualType DstTy, QualType SrcTy, + std::function &MergeFunctor, + QualType OrigDstTy) { + if (!DstTy->isPointerType()) + return DstTy; + + if (OrigDstTy.isNull()) + OrigDstTy = DstTy; + + // FIXME: a brittle hack to avoid skipping ValueTerminatedType outside + // this PtrAutoAttr AttributedType. + bool RecoverPtrAuto = false; + if (const auto *VT = dyn_cast(DstTy)) { + const auto *AT = DstTy->getAs(); + if (AT && VT->hasAttr(attr::PtrAutoAttr)) { + RecoverPtrAuto = true; + } + } + + const auto *AT = DstTy->getAs(); + if (AT && !RecoverPtrAuto) { + auto ModifiedTy = mergeBoundsSafetyPointerTypes( + AT->getModifiedType(), SrcTy, MergeFunctor, OrigDstTy); + if (ModifiedTy.isNull()) + return QualType(); + auto EquivalentTy = mergeBoundsSafetyPointerTypes( + AT->getEquivalentType(), SrcTy, MergeFunctor, OrigDstTy); + + auto QualsOnT = DstTy.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + auto NewDstTy = + getAttributedType(AT->getAttrKind(), ModifiedTy, EquivalentTy); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + NewDstTy = QC.apply(*this, NewDstTy); + } + // Make sure we don't lose ValueTerminatedType + NewDstTy = assureMandatorySugarTypesRemain(*this, NewDstTy, DstTy); + return NewDstTy; + } + + QualType DstPointeeTy = DstTy->getPointeeType(); + QualType SrcPointeeTy = + !SrcTy.isNull() ? SrcTy->getPointeeType() : QualType(); + QualType MergePointeeTy = + mergeBoundsSafetyPointerTypes(DstPointeeTy, SrcPointeeTy, MergeFunctor); + QualType MergeTy = MergeFunctor(DstTy, SrcTy, MergePointeeTy, OrigDstTy); + if (DstTy != MergeTy && !MergeTy.isNull()) { + if (RecoverPtrAuto) { + MergeTy = getAttributedType(attr::PtrAutoAttr, MergeTy, MergeTy); + } + auto QualsOnT = DstTy.getQualifiers(); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + MergeTy = QC.apply(*this, MergeTy); + } + } + return MergeTy; +} + +namespace { + +class MakeAutoPointer final + : public TypeVisitor> { +private: + using RetTy = std::pair; + using BaseClass = TypeVisitor; + + ASTContext &Ctx; + const BoundsSafetyPointerAttributes AbiFAttr; + std::optional AutoPtrAttr; + bool IsInsideValueTerminated = false; + + QualType copyQuals(QualType OldTy, QualType NewTy) const { + Qualifiers Qs = OldTy.getLocalQualifiers(); + if (const auto *AT = OldTy->getAs()) { + Qualifiers ModifiedTyQs = AT->getModifiedType().getQualifiers(); + Qualifiers::removeCommonQualifiers(Qs, ModifiedTyQs); + } + QualifierCollector QC(Qs); + return QC.apply(Ctx, NewTy); + } + +public: + explicit MakeAutoPointer( + ASTContext &Ctx, BoundsSafetyPointerAttributes AbiFAttr, + std::optional AutoPtrAttr) + : Ctx(Ctx), AbiFAttr(AbiFAttr), AutoPtrAttr(AutoPtrAttr) {} + + QualType Visit(QualType T) { + // We don't apply an implicit -fbounds-safety attribute for va_list, + // leaving it unsafe. + if (T->isAnyVaListType(Ctx)) + return T; + + QualType NewTy; + bool AddPtrAutoAttr; + std::tie(NewTy, AddPtrAutoAttr) = BaseClass::Visit(T.getTypePtr()); + + if (NewTy.isNull()) + return QualType(); + + NewTy = copyQuals(T, NewTy); + + if (AddPtrAutoAttr) + NewTy = Ctx.getAttributedType(attr::PtrAutoAttr, NewTy, NewTy); + + return NewTy; + } + + RetTy VisitType(const Type *T) { + QualType NewTy(T, 0); + return {NewTy, false}; + } + + RetTy VisitParenType(const ParenType *T) { + QualType InnerTy = Visit(T->getInnerType()); + QualType NewTy = Ctx.getParenType(InnerTy); + return {NewTy, false}; + } + + RetTy VisitAtomicType(const AtomicType *T) { + QualType ValueTy = Visit(T->getValueType()); + QualType NewTy = Ctx.getAtomicType(ValueTy); + return {NewTy, false}; + } + + RetTy VisitAttributedType(const AttributedType *T) { + auto SavedAutoPtrAttr = AutoPtrAttr; + QualType ModifiedTy = Visit(T->getModifiedType()); + AutoPtrAttr = SavedAutoPtrAttr; + QualType EquivalentTy = Visit(T->getEquivalentType()); + QualType NewTy = + Ctx.getAttributedType(T->getAttrKind(), ModifiedTy, EquivalentTy); + return {NewTy, false}; + } + + RetTy VisitTypedefType(const TypedefType *T) { + // If the underlying type after auto-bounding doesn't change, we retain the + // typedef type. However, if auto-bounding adds an attribute to the + // underlying type, we drop the typedef type, since otherwise we would have + // multiple inconsistent definitions of the typedef. + QualType UnderlyingTy = T->desugar(); + QualType NewUnderlyingTy = Visit(UnderlyingTy); + if (NewUnderlyingTy == UnderlyingTy) { + QualType NewTy = Ctx.getTypedefType(T->getDecl(), UnderlyingTy); + return {NewTy, false}; + } + return {NewUnderlyingTy, false}; + } + + RetTy VisitMacroQualifiedType(const MacroQualifiedType *T) { + QualType UnderlyingTy = T->desugar(); + QualType NewUnderlyingTy = Visit(UnderlyingTy); + QualType NewTy = + Ctx.getMacroQualifiedType(NewUnderlyingTy, T->getMacroIdentifier()); + return {NewTy, false}; + } + + RetTy VisitElaboratedType(const ElaboratedType *T) { + QualType NamedTy = T->getNamedType(); + QualType NewNamedTy = Visit(NamedTy); + if (NewNamedTy == NamedTy) { + return {QualType(T, 0), false}; + } + return {NewNamedTy, false}; + } + + RetTy VisitDecayedType(const DecayedType *T) { + // Replace the decayed type by an equivalent pointer type. + // TODO(pstefanski): We could retain the decayed type after some + // modifications to ASTContext. + auto SavedAutoPtrAttr = AutoPtrAttr; + AutoPtrAttr = std::nullopt; + QualType PointeeTy = Visit(T->getPointeeType()); + const auto *PtrTy = + cast(Ctx.getPointerType(PointeeTy).getTypePtr()); + AutoPtrAttr = SavedAutoPtrAttr; + return VisitPointerType(PtrTy); + } + + RetTy VisitConstantArrayType(const ConstantArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getConstantArrayType( + EltTy, T->getSize(), T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitDependentSizedArrayType(const DependentSizedArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getDependentSizedArrayType( + EltTy, T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers(), T->getBracketsRange()); + return {NewTy, false}; + } + + RetTy VisitIncompleteArrayType(const IncompleteArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getIncompleteArrayType(EltTy, T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitVariableArrayType(const VariableArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getVariableArrayType( + EltTy, T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers(), T->getBracketsRange()); + return {NewTy, false}; + } + + RetTy VisitPointerType(const PointerType *T) { + // Should we add PtrAutoAttr to this pointer upon returning? + bool AddPtrAutoAttr = false; + + auto PtrAttr = AutoPtrAttr; + + QualType PointeeTy; + { + AutoPtrAttr = std::nullopt; + llvm::SaveAndRestore IsInsideValueTerminatedSAR( + IsInsideValueTerminated, false); + PointeeTy = Visit(T->getPointeeType()); + } + + BoundsSafetyPointerAttributes Attr = T->getPointerAttributes(); + if (Attr.isUnspecified()) { + AddPtrAutoAttr = true; + // The following rules are to make function pointers and non-ABI visible + // pointers to have appropriate safe pointers (i.e., __single and + // __bidi_indexable, respectively). However, we shouldn't apply this rule + // when the default ABI-visible attribute is unsafe, as that can + // automatically create both unsafe and safe pointers in the same + // function, which are not compatible, leading to undesirable diagnostic + // errors on the pointer casts. Therefore, when the default ABI-visible + // pointer attribute is unsafe, we apply the same attribute to non-ABI + // visible pointers. + // + // Function pointers, which don't have a size, are unconditionally + // __single. + if (PointeeTy->isFunctionType() && !AbiFAttr.isUnsafeOrUnspecified()) + Attr.setSingle(); + else if (PtrAttr.has_value() && + (IsInsideValueTerminated || !AbiFAttr.isUnsafeOrUnspecified())) { + // Auto-bounding the outer-most pointer types that are not ABI-visible. + // FIXME: Should be set to AutoBound when we unlock the tree transform + // for escaping locals to implicitly transform them to __single. + // rdar://69452444 + Attr = *PtrAttr; + } else { + // ABI-visible pointers get the default ABI-visible BoundsSafety + // attributes. This is unsafe indexable in system headers and single + // otherwise. + Attr.copyBoundsAttr(AbiFAttr); + } + } + + QualType NewTy = Ctx.getPointerType(PointeeTy, Attr); + + bool IsPointeeChar = PointeeTy.getTypePtr() == Ctx.CharTy.getTypePtr(); + bool IsPointeeWChar = false; + if (const auto *TT = PointeeTy->getAs()) + IsPointeeWChar = TT->getDecl()->getName() == "wchar_t"; + if (AddPtrAutoAttr && Attr.isSingle() && PointeeTy.isConstQualified() && + (IsPointeeChar || IsPointeeWChar) && !IsInsideValueTerminated) { + unsigned IntSize = Ctx.getTargetInfo().getIntWidth(); + auto *ZeroLit = IntegerLiteral::Create(Ctx, llvm::APInt(IntSize, 0), + Ctx.IntTy, SourceLocation()); + auto *TermExpr = ImplicitCastExpr::Create( + Ctx, PointeeTy, CK_IntegralCast, ZeroLit, + /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); + NewTy = Ctx.getValueTerminatedType(NewTy, TermExpr); + NewTy = + Ctx.getAttributedType(attr::PtrAutoNullTerminatedAttr, NewTy, NewTy); + } + + return {NewTy, AddPtrAutoAttr}; + } + + RetTy VisitBoundsAttributedType(const BoundsAttributedType *T) { + AutoPtrAttr = std::nullopt; + QualType DesugaredTy = T->desugar(); + QualType PtrTy = Visit(DesugaredTy); + if (PtrTy == DesugaredTy) + return {QualType(T, 0), false}; + QualType NewTy; + if (const auto *DCPT = dyn_cast(T)) { + NewTy = Ctx.getCountAttributedType( + PtrTy, DCPT->getCountExpr(), DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } else if (const auto *DRPT = dyn_cast(T)) { + NewTy = Ctx.getDynamicRangePointerType( + PtrTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } else { + llvm_unreachable("Unknown BoundsAttributedType"); + } + return {NewTy, false}; + } + + RetTy VisitValueTerminatedType(const ValueTerminatedType *T) { + // Make value-terminated pointers __single by default. + AutoPtrAttr = BoundsSafetyPointerAttributes::single(); + llvm::SaveAndRestore IsInsideValueTerminatedSAR( + IsInsideValueTerminated, true); + QualType DesugaredTy = T->desugar(); + // There shouldn't be nested value-terminated types. + assert(!DesugaredTy->getAs()); + QualType NewDesugaredTy = Visit(DesugaredTy); + assert(!NewDesugaredTy->getAs()); + QualType NewTy = + Ctx.getValueTerminatedType(NewDesugaredTy, T->getTerminatorExpr()); + return {NewTy, false}; + } + + RetTy VisitFunctionProtoType(const FunctionProtoType *T) { + AutoPtrAttr = std::nullopt; + QualType ReturnTy = Visit(T->getReturnType()); + SmallVector ParamTypes; + ParamTypes.reserve(T->getNumParams()); + for (QualType ParamTy : T->getParamTypes()) + ParamTypes.push_back(Visit(ParamTy)); + QualType NewTy = + Ctx.getFunctionType(ReturnTy, ParamTypes, T->getExtProtoInfo()); + return {NewTy, false}; + } + + RetTy VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + AutoPtrAttr = std::nullopt; + QualType ReturnTy = Visit(T->getReturnType()); + QualType NewTy = Ctx.getFunctionNoProtoType(ReturnTy, T->getExtInfo()); + return {NewTy, false}; + } +}; + +} // namespace + +QualType ASTContext::getBoundsSafetyAutoPointerType( + QualType T, BoundsSafetyPointerAttributes AbiFAttr, bool ShouldAutoBound) { + // XXX: We don't apply an implicit -fbounds-safety attribute for va_list, which + // leaves va_list unsafe + if (T->isAnyVaListType(*this)) + return T; + + std::optional AutoPtrAttr; + if (ShouldAutoBound) + AutoPtrAttr = BoundsSafetyPointerAttributes::bidiIndexable(); + MakeAutoPointer MAP(*this, AbiFAttr, AutoPtrAttr); + return MAP.Visit(T); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + QualType ASTContext::removePtrSizeAddrSpace(QualType T) const { if (const PointerType *Ptr = T->getAs()) { QualType Pointee = Ptr->getPointeeType(); if (isPtrSizeAddressSpace(Pointee.getAddressSpace())) { - return getPointerType(removeAddrSpaceQualType(Pointee)); + return getPointerType(removeAddrSpaceQualType(Pointee), + Ptr->getPointerAttributes()); } } return T; @@ -3652,11 +4155,12 @@ QualType ASTContext::getComplexType(QualType T) const { /// getPointerType - Return the uniqued reference to the type for a pointer to /// the specified type. -QualType ASTContext::getPointerType(QualType T) const { +QualType ASTContext::getPointerType(QualType T, + BoundsSafetyPointerAttributes A) const { // Unique pointers, to guarantee there is only one pointer of a particular // structure. llvm::FoldingSetNodeID ID; - PointerType::Profile(ID, T); + PointerType::Profile(ID, T, A); void *InsertPos = nullptr; if (PointerType *PT = PointerTypes.FindNodeOrInsertPos(ID, InsertPos)) @@ -3666,13 +4170,13 @@ QualType ASTContext::getPointerType(QualType T) const { // so fill in the canonical type field. QualType Canonical; if (!T.isCanonical()) { - Canonical = getPointerType(getCanonicalType(T)); + Canonical = getPointerType(getCanonicalType(T), A); // Get the new insert position for the node we care about. PointerType *NewIP = PointerTypes.FindNodeOrInsertPos(ID, InsertPos); assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - auto *New = new (*this, alignof(PointerType)) PointerType(T, Canonical); + auto *New = new (*this, alignof(PointerType)) PointerType(T, Canonical, A); Types.push_back(New); PointerTypes.InsertNode(New, InsertPos); return QualType(New, 0); @@ -6651,6 +7155,53 @@ bool ASTContext::hasCvrSimilarType(QualType T1, QualType T2) { } } +bool ASTContext::hasSameBoundsSafetyPointerLayout(QualType T1, QualType T2) { + return hasCompatibleBoundsSafetyPointerLayout(T1, T2, /*ExactCheck*/ true); +} + +bool ASTContext::hasCompatibleBoundsSafetyPointerLayout(QualType T1, QualType T2, + bool ExactCheck) { + while (true) { + if (ExactCheck && (T1->getAs() + != T2->getAs())) + return false; + Qualifiers Quals; + T1 = getUnqualifiedArrayType(T1, Quals); + T2 = getUnqualifiedArrayType(T2, Quals); + + if (hasSameType(T1, T2)) + return true; + + const auto *PT1 = T1->getBaseElementTypeUnsafe()->getAs(); + const auto *PT2 = T2->getBaseElementTypeUnsafe()->getAs(); + + if (!PT1 && !PT2) + return true; + + if (PT1 && PT2) { + if (!ExactCheck) { + if (!BoundsSafetyPointerAttributes::areCompatible( + PT1->getPointerAttributes(), PT2->getPointerAttributes())) + return false; + } else if (PT1->getPointerAttributes() != PT2->getPointerAttributes()) + return false; + } + + if (PT1) { + if (!PT2 && PT1->isSafePointer()) + return false; + + T1 = PT1->getPointeeType(); + } + + if (PT2) { + if (!PT1 && PT2->isSafePointer()) + return false; + T2 = PT2->getPointeeType(); + } + } +} + DeclarationNameInfo ASTContext::getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const { @@ -11177,6 +11728,15 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, if (LHSRefTy || RHSRefTy) return {}; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *RProto = RHS->getAs()) { + // Do not merge bounds attributed return types since they are referring to + // different decls. + if (RProto->getReturnType()->isBoundsAttributedType()) + return {}; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (Unqualified) { LHS = LHS.getUnqualifiedType(); RHS = RHS.getUnqualifiedType(); @@ -11304,8 +11864,18 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, case Type::Pointer: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->castAs()->getPointeeType(); - QualType RHSPointee = RHS->castAs()->getPointeeType(); + const PointerType *LHSPointer = LHS->castAs(); + const PointerType *RHSPointer = RHS->castAs(); + BoundsSafetyPointerAttributes LHSFA = LHSPointer->getPointerAttributes(); + BoundsSafetyPointerAttributes RHSFA = RHSPointer->getPointerAttributes(); + if (!BoundsSafetyPointerAttributes::areCompatible(LHSFA, RHSFA)) + return {}; + BoundsSafetyPointerAttributes BestFA = LHSFA; + if (BestFA.isUnspecified()) + BestFA = RHSFA; + + QualType LHSPointee = LHSPointer->getPointeeType(); + QualType RHSPointee = RHSPointer->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -11314,11 +11884,16 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, Unqualified); if (ResultType.isNull()) return {}; + QualType BestType; if (getCanonicalType(LHSPointee) == getCanonicalType(ResultType)) - return LHS; - if (getCanonicalType(RHSPointee) == getCanonicalType(ResultType)) - return RHS; - return getPointerType(ResultType); + BestType = LHS; + else if (getCanonicalType(RHSPointee) == getCanonicalType(ResultType)) + BestType = RHS; + else + BestType = getPointerType(ResultType, BestFA); + if (BestType->getAs()->getPointerAttributes() != BestFA) + BestType = getBoundsSafetyPointerType(BestType, BestFA); + return BestType; } case Type::BlockPointer: { @@ -13428,7 +14003,15 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, } case Type::Pointer: { const auto *PX = cast(X), *PY = cast(Y); - return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY)); + // Bounds safety: pointer attributes are not sugar but they are part of + // canonical types. Hence, the attributes must be same and this function + // must preserve them. + auto PXFAttr = PX->getPointerAttributes(); + auto PYFAttr = PY->getPointerAttributes(); + (void)PYFAttr; + assert(PXFAttr == PYFAttr || + PXFAttr.isUnsafeOrUnspecified() == PYFAttr.isUnsafeOrUnspecified()); + return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY), PXFAttr); } case Type::BlockPointer: { const auto *PX = cast(X), *PY = cast(Y); @@ -13876,18 +14459,47 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, return Ctx.getCountAttributedType(Ctx.getQualifiedType(Underlying), CEX, DX->isCountInBytes(), DX->isOrNull(), CDX); + /* TO_UPSTREAM(BoundsSafety) ON */ if (!CEX->isIntegerConstantExpr(Ctx) || !CEY->isIntegerConstantExpr(Ctx)) - return QualType(); + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Two declarations with the same integer constant may still differ in their // expression pointers, so we need to evaluate them. llvm::APSInt VX = *CEX->getIntegerConstantExpr(Ctx); llvm::APSInt VY = *CEY->getIntegerConstantExpr(Ctx); + /* TO_UPSTREAM(BoundsSafety) ON */ if (VX != VY) - return QualType(); + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + return Ctx.getCountAttributedType(Ctx.getQualifiedType(Underlying), CEX, DX->isCountInBytes(), DX->isOrNull(), CDX); } + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: { + const auto *DX = cast(X), + *DY = cast(Y); + if (!Ctx.hasSameExpr(DX->getStartPointer(), DY->getStartPointer()) || + !Ctx.hasSameExpr(DX->getEndPointer(), DY->getEndPointer())) + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + assert(DX->getNumStartPtrDecls() == DY->getNumStartPtrDecls()); + assert(DX->getNumEndPtrDecls() == DY->getNumEndPtrDecls()); + return Ctx.getDynamicRangePointerType( + Ctx.getQualifiedType(Underlying), DX->getStartPointer(), + DX->getEndPointer(), DX->getStartPtrDecls(), DX->getEndPtrDecls()); + } + case Type::ValueTerminated: { + const auto *VX = cast(X), + *VY = cast(Y); + if (!Ctx.hasSameExpr(VX->getTerminatorExpr(), VY->getTerminatorExpr()) && + VX->getTerminatorValue(Ctx) != VY->getTerminatorValue(Ctx)) + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + return Ctx.getValueTerminatedType(Ctx.getQualifiedType(Underlying), + VX->getTerminatorExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ } llvm_unreachable("Unhandled Type Class"); } @@ -13905,9 +14517,169 @@ static auto unwrapSugar(SplitQualType &T, Qualifiers &QTotal) { return R; } +/* TO_UPSTREAM(BoundsSafety) ON */ +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeFunctionTypeBounds(const FunctionProtoType *LHSTy, + const FunctionProtoType *RHSTy) const{ + if (LHSTy->getNumParams() != RHSTy->getNumParams()) + return ASTContext::BSPTMK_FunctionTypeMismatch; + for (unsigned i = 0; i < LHSTy->getNumParams(); i++) { + if (!canMergeInnerTypeBounds(LHSTy->getParamTypes()[i], + RHSTy->getParamTypes()[i])) + return ASTContext::BSPTMK_FunctionTypeMismatch; + } + if (canMergeInnerTypeBounds(LHSTy->getReturnType(), RHSTy->getReturnType()) != + ASTContext::BSPTMK_CanMerge) + return ASTContext::BSPTMK_FunctionTypeMismatch; + return ASTContext::BSPTMK_CanMerge; +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeInnerTypeBounds(QualType LHSTy, QualType RHSTy) const { + assert(!!LHSTy->getAs() == !!RHSTy->getAs()); + + if (LHSTy->isFunctionProtoType()) { + return canMergeFunctionTypeBounds(LHSTy->getAs(), + RHSTy->getAs()); + } + + if (!LHSTy->getAs()) + return ASTContext::BSPTMK_CanMerge; + + while (LHSTy->getTypeClass() != Type::Pointer) { + switch (LHSTy->getTypeClass()) { + case Type::CountAttributed: { + auto LTy = cast(LHSTy); + auto RTy = RHSTy->getAs(); + if (!RTy) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (LTy->isCountInBytes() != RTy->isCountInBytes()) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (LTy->isOrNull() != RTy->isOrNull()) + return ASTContext::BSPTMK_NestedBoundsMismatch; + const auto CEL = LTy->getCountExpr(); + const auto CER = RTy->getCountExpr(); + if (!hasSameExpr(CEL, CER) && (!CEL->isIntegerConstantExpr(*this) || + !CER->isIntegerConstantExpr(*this) || + CEL->getIntegerConstantExpr(*this) != + CER->getIntegerConstantExpr(*this))) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + case Type::DynamicRangePointer: { + auto LTy = cast(LHSTy); + auto RTy = RHSTy->getAs(); + if (!RTy) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (!hasSameExpr(LTy->getStartPointer(), RTy->getStartPointer()) || + !hasSameExpr(LTy->getEndPointer(), RTy->getEndPointer())) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + case Type::ValueTerminated: { + if (checkTerminatedByMismatch(LHSTy, RHSTy) != BSPTMK_CanMerge) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + default: + // Non-bounds sugar: don't iterate RHS + auto LHSNext = LHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(LHSNext != LHSTy); + LHSTy = LHSNext; + continue; + } + auto LHSNext = LHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(LHSNext != LHSTy); + auto CurrentTypeClass = LHSTy->getTypeClass(); + LHSTy = LHSNext; + + auto RHSNext = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + // Iterate until the RHSTy is at the same bounds sugar type as the + // LHS was, and then once more to match in the next iteration + while (RHSTy->getTypeClass() != CurrentTypeClass) { + assert(RHSNext != RHSTy); + auto RHSTypeClass = RHSTy->getTypeClass(); + if (RHSTypeClass == Type::CountAttributed || + RHSTypeClass == Type::DynamicRangePointer || + RHSTypeClass == Type::ValueTerminated) + return ASTContext::BSPTMK_NestedBoundsMismatch; // We found bounds + // sugar on the RHS that + // we haven't seen on + // the LHS + RHSTy = RHSNext; + RHSNext = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + } + RHSTy = RHSNext; + } + + // We reached the LHS raw pointer type without reaching any mismatching + // bounds sugar nodes. Make sure the RHS doesn't have any bounds sugar + // left. + while (RHSTy->getTypeClass() != Type::Pointer) { + switch (RHSTy->getTypeClass()) { + case Type::CountAttributed: + case Type::DynamicRangePointer: + case Type::ValueTerminated: + return ASTContext::BSPTMK_NestedBoundsMismatch; + default: + auto Next = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(Next != RHSTy); + RHSTy = Next; + } + } + + // Check next layer in case of deeply nested pointer types + return canMergeInnerTypeBounds(cast(LHSTy)->getPointeeType(), + cast(RHSTy)->getPointeeType()); +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::checkTerminatedByMismatch(QualType LHSTy, QualType RHSTy) const { + auto LVTTy = LHSTy->getAs(); + auto RVTTy = RHSTy->getAs(); + if (!!LVTTy != !!RVTTy) + return ASTContext::BSPTMK_TerminatedByMismatch; + + if (LVTTy && RVTTy) { + if (LVTTy->getTerminatorValue(*this) != RVTTy->getTerminatorValue(*this)) + return ASTContext::BSPTMK_TerminatedByMismatch; + } + + return BSPTMK_CanMerge; +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeTypeBounds(QualType LHSTy, QualType RHSTy) const { + assert(getCanonicalType(LHSTy).getUnqualifiedType() == + getCanonicalType(RHSTy).getUnqualifiedType()); + assert(getLangOpts().BoundsSafety); + + auto TermCheck = checkTerminatedByMismatch(LHSTy, RHSTy); + if (TermCheck != BSPTMK_CanMerge) + return TermCheck; + + auto LHSPointerTy = LHSTy->getAs(); + auto RHSPointerTy = RHSTy->getAs(); + assert(!!LHSPointerTy == !!RHSPointerTy); + if (LHSPointerTy) { + // We don't need to handle outermost layer of pointer type annotations; + // they may be bounds checked dynamically while this is only for static type + // merging. + LHSTy = LHSPointerTy->getPointeeType(); + RHSTy = RHSPointerTy->getPointeeType(); + } + + return canMergeInnerTypeBounds(LHSTy, RHSTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + QualType ASTContext::getCommonSugaredType(QualType X, QualType Y, bool Unqualified) { assert(Unqualified ? hasSameUnqualifiedType(X, Y) : hasSameType(X, Y)); + /* TO_UPSTREAM(BoundsSafety) ON */ + assert(Unqualified || !getLangOpts().BoundsSafety || + canMergeTypeBounds(X, Y) == ASTContext::BSPTMK_CanMerge); + /* TO_UPSTREAM(BoundsSafety) OFF */ if (X == Y) return X; if (!Unqualified) { @@ -13965,6 +14737,14 @@ QualType ASTContext::getCommonSugaredType(QualType X, QualType Y, // with the sugar nodes we could not unify. QualType R = getQualifiedType(SX.Ty, QX); assert(Unqualified ? hasSameUnqualifiedType(R, X) : hasSameType(R, X)); + + // Other sugar types failing to unify can prevent bounds safety sugar from + // unifying also. Losing this sugar affects semantics, so force it back on. + R = assureMandatorySugarTypesRemain(*this, R, X); + assert(R->isValueTerminatedType() == Y->isValueTerminatedType()); + assert(R->isCountAttributedType() == Y->isCountAttributedType()); + assert(R->isDynamicRangePointerType() == Y->isDynamicRangePointerType()); + return R; } diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index c4fd94612e4c4..1431e37db641c 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -210,7 +210,8 @@ break; \ // FIXME: Handle other pointer-like types. if (const PointerType *Ty = QT->getAs()) { QT = Context.getPointerType( - desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); + desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA), + Ty->getPointerAttributes()); } else if (const auto *Ty = QT->getAs()) { QT = Context.getObjCObjectPointerType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index dcbecd22bd099..572f9494184b4 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1164,7 +1164,8 @@ ExpectedType ASTNodeImporter::VisitPointerType(const PointerType *T) { if (!ToPointeeTypeOrErr) return ToPointeeTypeOrErr.takeError(); - return Importer.getToContext().getPointerType(*ToPointeeTypeOrErr); + return Importer.getToContext().getPointerType(*ToPointeeTypeOrErr, + T->getPointerAttributes()); } ExpectedType ASTNodeImporter::VisitBlockPointerType(const BlockPointerType *T) { @@ -1567,6 +1568,20 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) { ArrayRef(CoupledDecls.data(), CoupledDecls.size())); } +/* TO_UPSTREAM(BoundsSafety) ON */ +ExpectedType clang::ASTNodeImporter::VisitDynamicRangePointerType( + const clang::DynamicRangePointerType *T) { + // FIXME: Unsupported AST node. + return VisitType(T); +} + +ExpectedType clang::ASTNodeImporter::VisitValueTerminatedType( + const clang::ValueTerminatedType *T) { + // FIXME: Unsupported AST node. + return VisitType(T); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExpectedType ASTNodeImporter::VisitTemplateTypeParmType( const TemplateTypeParmType *T) { Expected ToDeclOrErr = import(T->getDecl()); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 37555c324282f..15d1033bb3368 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -856,12 +856,20 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; - case Type::Pointer: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getPointeeType(), - cast(T2)->getPointeeType())) + case Type::Pointer: { + /* TO_UPSTREAM(BoundsSafety) ON*/ + const PointerType *PT1 = cast(T1); + const PointerType *PT2 = cast(T2); + + if (!IsStructurallyEquivalent(Context, PT1->getPointeeType(), + PT2->getPointeeType())) + return false; + if (!BoundsSafetyPointerAttributes::areEquivalentLayouts( + PT1->getPointerAttributes(), PT2->getPointerAttributes())) return false; break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } case Type::BlockPointer: if (!IsStructurallyEquivalent(Context, @@ -1086,6 +1094,22 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + if (!IsStructurallyEquivalent( + Context, cast(T1)->desugar(), + cast(T2)->desugar())) + return false; + break; + + case Type::ValueTerminated: + if (!IsStructurallyEquivalent(Context, + cast(T1)->desugar(), + cast(T2)->desugar())) + return false; + break; + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Type::BTFTagAttributed: if (!IsStructurallyEquivalent( Context, cast(T1)->getWrappedType(), diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index f198a9acf8481..1e7dfa82b7170 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -270,4 +270,30 @@ unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const { return Ctx.getTargetDefaultAlignForAttributeAligned(); } +bool clang::isValidPointerAttrType(QualType T, bool RefOkay) { + if (T->isDependentType()) + return true; + if (RefOkay) { + if (T->isReferenceType()) + return true; + } else { + T = T.getNonReferenceType(); + } + + // The nonnull attribute, and other similar attributes, can be applied to a + // transparent union that contains a pointer type. + if (const RecordType *UT = T->getAsUnionType()) { + if (UT && UT->getDecl()->hasAttr()) { + RecordDecl *UD = UT->getDecl(); + for (const auto *I : UD->fields()) { + QualType QT = I->getType(); + if (QT->isAnyPointerType() || QT->isBlockPointerType()) + return true; + } + } + } + + return T->isAnyPointerType() || T->isBlockPointerType(); +} + #include "clang/AST/AttrImpl.inc" diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 62ca15ea398f5..ac114a25b5f5a 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -21,6 +21,20 @@ using namespace clang; +/* TO_UPSTREAM(BoundsSafety) ON*/ +namespace { + template + ExprDependence SumDependence(Range R) { + auto Result = ExprDependence::None; + for (const auto *E : R) { + if (E) + Result |= cast(E)->getDependence(); + } + return Result; + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + ExprDependence clang::computeDependence(FullExpr *E) { return E->getSubExpr()->getDependence(); } @@ -883,6 +897,37 @@ ExprDependence clang::computeDependence(MaterializeTemporaryExpr *E) { return E->getSubExpr()->getDependence(); } +ExprDependence clang::computeDependence(MaterializeSequenceExpr *E) { + // All opaque values managed by MaterializeSequenceExpr should be used by the + // main sub-expression, therefore it is the only visitation root we need to + // respect. + return E->getWrappedExpr()->getDependence(); +} + +ExprDependence clang::computeDependence(PredefinedBoundsCheckExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(BoundsCheckExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(AssumptionExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(BoundsSafetyPointerPromotionExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(GetBoundExpr *E) { + return E->getSubExpr()->getDependence(); +} + +ExprDependence clang::computeDependence(ForgePtrExpr *E) { + return SumDependence(E->children()); +} + ExprDependence clang::computeDependence(CXXFoldExpr *E) { auto D = ExprDependence::TypeValueInstantiation; for (const auto *C : {E->getLHS(), E->getRHS()}) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 278902dc45ecf..cfbb3393183df 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -203,6 +203,22 @@ static bool isExplicitMemberSpecialization(const RedeclarableTemplateDecl *D) { return D->isMemberSpecialization(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +// A BoundsSafety helper function. +bool clang::IsConstOrLateConst(const Decl *D) { + if (D->hasAttr()) + return true; + + auto const *VD = dyn_cast(D); + if (!VD) + return false; + + if (VD->getType().isConstQualified()) + return true; + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Given a visibility attribute, return the explicit visibility /// associated with it. template @@ -5059,6 +5075,21 @@ bool RecordDecl::isOrContainsUnion() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +bool RecordDecl::isParentStructOf(const Decl *Inner) const { + auto DeclCtx = Inner->getDeclContext(); + if (DeclCtx == this) + return true; + for (auto FD : fields()) { + if (auto InnerStructTy = FD->getType()->getAs()) { + if (cast(InnerStructTy->getDecl())->isParentStructOf(Inner)) + return true; + } + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + RecordDecl::field_iterator RecordDecl::field_begin() const { if (hasExternalLexicalStorage() && !hasLoadedFieldsFromExternalStorage()) LoadFieldsFromExternalStorage(); @@ -5367,6 +5398,36 @@ bool ValueDecl::isInitCapture() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool ValueDecl::isDependentParamOfReturnType( + const BoundsAttributedType **RetType, + const TypeCoupledDeclRefInfo **Info) const { + const auto *PVD = dyn_cast(this); + if (!PVD) + return false; + + const auto *FD = dyn_cast(PVD->getDeclContext()); + if (!FD) + return false; + + const auto *BATy = FD->getReturnType()->getAs(); + if (!BATy) + return false; + + auto *It = std::find_if( + BATy->dependent_decl_begin(), BATy->dependent_decl_end(), + [PVD](const TypeCoupledDeclRefInfo &I) { return I.getDecl() == PVD; }); + if (It == BATy->dependent_decl_end()) + return false; + + if (RetType) + *RetType = BATy; + if (Info) + *Info = &*It; + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void ImplicitParamDecl::anchor() {} ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 9d5b8167d0ee6..2bba4af77a951 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1855,6 +1855,11 @@ bool CastExpr::CastConsistency() const { } goto CheckNoBasePath; + case CK_BoundsSafetyPointerCast: + assert(getType()->isPointerType()); + assert(getSubExpr()->getType()->isPointerType()); + goto CheckNoBasePath; + case CK_AnyPointerToBlockPointerCast: assert(getType()->isBlockPointerType()); assert(getSubExpr()->getType()->isAnyPointerType() && @@ -1958,6 +1963,53 @@ const char *CastExpr::getCastKindName(CastKind CK) { } namespace { + /* TO_UPSTREAM(BoundsSafety) ON */ + const Expr *skipImplicitTemporary( + const Expr *E, + llvm::SmallPtrSetImpl &BoundValues) { + const Expr *Prev; + do { + Prev = E; + // Skip through reference binding to temporary. + if (auto *Materialize = dyn_cast(E)) + E = Materialize->getSubExpr(); + + // Skip any temporary bindings; they're implicit. + if (auto *Binder = dyn_cast(E)) + E = Binder->getSubExpr(); + + if (auto *MSE = dyn_cast(E)) { + if (!MSE->isUnbinding()) { + BoundValues.insert(MSE->opaquevalues_begin(), + MSE->opaquevalues_end()); + } + E = MSE->getWrappedExpr(); + } + + if (auto *Opaque = dyn_cast(E)) + if (BoundValues.count(Opaque) != 0) + E = Opaque->getSourceExpr(); + + if (auto *BCE = dyn_cast(E)) { + BoundValues.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + E = BCE->getGuardedExpr(); + } + + if (auto *FPPE = dyn_cast(E)) + E = FPPE->getSubExpr(); + + if (auto *AE = dyn_cast(E)) + E = AE->getWrappedExpr(); + } while (Prev != E); + return E; + } + + const Expr *skipImplicitTemporary(const Expr *E) { + llvm::SmallPtrSet BoundValues; + return skipImplicitTemporary(E, BoundValues); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Skip over implicit nodes produced as part of semantic analysis. // Designed for use with IgnoreExprNodes. static Expr *ignoreImplicitSemaNodes(Expr *E) { @@ -2129,6 +2181,175 @@ CStyleCastExpr *CStyleCastExpr::CreateEmpty(const ASTContext &C, return new (Buffer) CStyleCastExpr(EmptyShell(), PathSize, HasFPFeatures); } +AssumptionExpr::AssumptionExpr(Expr *ResultExpr, + llvm::ArrayRef Assumptions) + : Expr(AssumptionExprClass, ResultExpr->getType(), + ResultExpr->getValueKind(), ResultExpr->getObjectKind()) { + AssumptionExprBits.NumExprs = Assumptions.size() + 1; + auto ExprPtr = getTrailingExprs(); + *ExprPtr = ResultExpr; + for (Expr *E : Assumptions) { + ++ExprPtr; + *ExprPtr = E; + } + setDependence(computeDependence(this)); +} + +AssumptionExpr *AssumptionExpr::Create(const ASTContext &Ctx, Expr *Result, + ArrayRef Assumptions) { + const unsigned NumExprs = Assumptions.size() + 1; + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(AssumptionExpr)); + return new (Mem) AssumptionExpr(Result, Assumptions); +} + +AssumptionExpr *AssumptionExpr::CreateEmpty(const ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(AssumptionExpr)); + return new (Mem) AssumptionExpr(EmptyShell(), NumExprs); +} + +Stmt **BoundsSafetyPointerPromotionExpr::getPointerPtr() { + return getTrailingObjects(); +} + +Stmt **BoundsSafetyPointerPromotionExpr::getLowerBoundPtr() { + auto FA = getType()->getAs()->getPointerAttributes(); + if (!FA.hasLowerBound()) + return nullptr; + return getPointerPtr() + 2; +} + +Stmt **BoundsSafetyPointerPromotionExpr::getUpperBoundPtr() { + return getPointerPtr() + 1; +} + +BoundsSafetyPointerPromotionExpr *BoundsSafetyPointerPromotionExpr::CreateEmpty( + const ASTContext &Context, unsigned SubExprCount) { + assert(SubExprCount >= 1 && "Missing Ptr SubExpr"); + void *Buffer = Context.Allocate(totalSizeToAlloc(SubExprCount), + alignof(BoundsSafetyPointerPromotionExpr)); + auto *FPPE = new (Buffer) BoundsSafetyPointerPromotionExpr(EmptyShell()); + return FPPE; +} + +BoundsSafetyPointerPromotionExpr * +BoundsSafetyPointerPromotionExpr::Create(const ASTContext &Context, QualType QT, + Expr *Ptr, Expr *UpperBound, + Expr *LowerBound, bool NullCheck) { + auto PtrTy = QT->getAs(); + auto FA = PtrTy->getPointerAttributes(); + assert(FA.hasUpperBound()); + unsigned Count = 1 + FA.hasLowerBound() + FA.hasUpperBound(); + void *Buffer = Context.Allocate(totalSizeToAlloc(Count), + alignof(BoundsSafetyPointerPromotionExpr)); + auto *FPPE = new (Buffer) + BoundsSafetyPointerPromotionExpr(QT, Ptr, UpperBound, LowerBound, NullCheck); + return FPPE; +} + +Expr *BoundsSafetyPointerPromotionExpr::getSubExprAsWritten() { + const Expr *SubExpr = skipImplicitTemporary(getSubExpr()); + while (const auto *E = dyn_cast(SubExpr)) { + SubExpr = skipImplicitTemporary(E->getSubExpr()); + } + return const_cast(SubExpr); +} + +PredefinedBoundsCheckExpr::PredefinedBoundsCheckExpr(Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) + : Expr(PredefinedBoundsCheckExprClass, GuardedExpr->getType(), + GuardedExpr->getValueKind(), GuardedExpr->getObjectKind()) { + PredefinedBoundsCheckExprBits.Kind = static_cast(Kind); + PredefinedBoundsCheckExprBits.NumChildren = + CheckArgs.size() + PredefinedBoundsCheckExpr::getCheckArgsOffset(); + + setGuardedExpr(GuardedExpr); + std::copy(CheckArgs.begin(), CheckArgs.end(), + getSubExprs() + PredefinedBoundsCheckExpr::getCheckArgsOffset()); + setDependence(computeDependence(this)); +} + +PredefinedBoundsCheckExpr * +PredefinedBoundsCheckExpr::Create(ASTContext &Ctx, Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) { + const unsigned NumExprs = CheckArgs.size() + getCheckArgsOffset(); + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(PredefinedBoundsCheckExpr)); + return new (Mem) PredefinedBoundsCheckExpr(GuardedExpr, Kind, CheckArgs); +} + +PredefinedBoundsCheckExpr * +PredefinedBoundsCheckExpr::CreateEmpty(ASTContext &Ctx, unsigned NumChildren) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumChildren), + alignof(PredefinedBoundsCheckExpr)); + return new (Mem) PredefinedBoundsCheckExpr(EmptyShell(), NumChildren); +} + +StringRef PredefinedBoundsCheckExpr::getKindName() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountDeref: + return "FlexibleArrayCountDeref(BasePtr, FamPtr, Count)"; + case BoundsCheckKind::FlexibleArrayCountCast: + return "FlexibleArrayCountCast(BasePtr, FamPtr, Count)"; + case BoundsCheckKind::FlexibleArrayCountAssign: + return "FlexibleArrayCountAssign(BasePtr, FamPtr, Count)"; + } + llvm_unreachable("Unexpected PredefinedBoundsCheckKind"); +} + +BoundsCheckExpr::BoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs) + : Expr(BoundsCheckExprClass, GuardedExpr->getType(), + GuardedExpr->getValueKind(), GuardedExpr->getObjectKind()) { + BoundsCheckExprBits.NumChildren = CommonExprs.size() + 2; + + setGuardedExpr(GuardedExpr); + setCond(Cond); + std::copy(CommonExprs.begin(), CommonExprs.end(), getSubExprs() + 2); + setDependence(computeDependence(this)); +} + +BoundsCheckExpr *BoundsCheckExpr::Create(ASTContext &Ctx, Expr *GuardedExpr, + Expr *Cond, + ArrayRef CommonExprs) { + const unsigned NumExprs = CommonExprs.size() + 2; + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(BoundsCheckExpr)); + return new (Mem) BoundsCheckExpr(GuardedExpr, Cond, CommonExprs); +} + +BoundsCheckExpr *BoundsCheckExpr::CreateEmpty(ASTContext &Ctx, + unsigned NumChildren) { + unsigned SizeOfTrailingObjects = NumChildren * sizeof(Stmt *); + void *Mem = Ctx.Allocate(sizeof(BoundsCheckExpr) + SizeOfTrailingObjects, + alignof(BoundsCheckExpr)); + return new (Mem) BoundsCheckExpr(EmptyShell(), NumChildren); +} + +MaterializeSequenceExpr *MaterializeSequenceExpr::CreateEmpty(const ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(MaterializeSequenceExpr)); + + return new (Mem) MaterializeSequenceExpr(EmptyShell(), NumExprs); +} + +MaterializeSequenceExpr *MaterializeSequenceExpr::Create(const ASTContext &Ctx, + Expr *WrappedExpr, + ArrayRef Values, + bool Unbind) { + const unsigned NumExprs = Values.size() + 1; + + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(MaterializeSequenceExpr)); + + return new (Mem) MaterializeSequenceExpr(WrappedExpr, Values, Unbind); +} + /// getOpcodeStr - Turn an Opcode enum value into the punctuation char it /// corresponds to, e.g. "<<=". StringRef BinaryOperator::getOpcodeStr(Opcode Op) { @@ -2597,15 +2818,36 @@ bool Expr::isReadIfDiscardedInCPlusPlus11() const { /// be warned about if the result is unused. If so, fill in Loc and Ranges /// with location to warn on and the source range[s] to report with the /// warning. +/* TO_UPSTREAM(BoundsSafety) ON*/ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, ASTContext &Ctx) const { + llvm::SmallPtrSet BoundExprs; + return isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx, + // TO_UPSTREAM(BoundsSafety) + llvm::SmallPtrSetImpl &BoundExprs) const { // Don't warn if the expr is type dependent. The type could end up // instantiating to void. if (isTypeDependent()) return false; switch (getStmtClass()) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + case OpaqueValueExprClass: { + const auto *OVE = cast(this); + if (BoundExprs.find(OVE) != BoundExprs.end()) { + return OVE->getSourceExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, BoundExprs); + } + LLVM_FALLTHROUGH; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ default: if (getType()->isVoidType()) return false; @@ -2615,17 +2857,17 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, return true; case ParenExprClass: return cast(this)->getSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case GenericSelectionExprClass: return cast(this)->getResultExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case CoawaitExprClass: case CoyieldExprClass: return cast(this)->getResumeExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case ChooseExprClass: return cast(this)->getChosenSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case UnaryOperatorClass: { const UnaryOperator *UO = cast(this); @@ -2653,7 +2895,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, BoundExprs); } WarnE = this; Loc = UO->getOperatorLoc(); @@ -2674,12 +2917,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, dyn_cast(BO->getRHS()->IgnoreParens())) if (IE->getValue() == 0) return false; - return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return BO->getRHS()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, BoundExprs); // Consider '||', '&&' to have side effects if the LHS or RHS does. case BO_LAnd: case BO_LOr: - if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) || - !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) + if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs) || + !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)) return false; break; } @@ -2701,12 +2947,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // be being used for control flow. Only warn if both the LHS and // RHS are warnings. const auto *Exp = cast(this); - return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) && - Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs) && + Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case BinaryConditionalOperatorClass: { const auto *Exp = cast(this); - return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case MemberExprClass: @@ -2878,10 +3127,10 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, const CompoundStmt *CS = cast(this)->getSubStmt(); if (!CS->body_empty()) { if (const Expr *E = dyn_cast(CS->body_back())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); if (const LabelStmt *Label = dyn_cast(CS->body_back())) if (const Expr *E = dyn_cast(Label->getSubStmt())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); } if (getType()->isVoidType()) @@ -2917,7 +3166,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, if (SubE->getType()->isArrayType()) return false; - return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } return false; } @@ -2925,7 +3175,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // If this is a cast to a constructor conversion, check the operand. // Otherwise, the result of the cast is unused. if (CE->getCastKind() == CK_ConstructorConversion) - return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); if (CE->getCastKind() == CK_Dependent) return false; @@ -2941,6 +3192,27 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, } return true; } + case BoundsSafetyPointerPromotionExprClass: + return cast(this) + ->getPointer() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); + case AssumptionExprClass: + return cast(this) + ->getWrappedExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); + case BoundsCheckExprClass: { + const auto *BCE = cast(this); + BoundExprs.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + return BCE->getGuardedExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, + Ctx, BoundExprs); + } + case MaterializeSequenceExprClass: { + const auto *MSE = cast(this); + if (!MSE->isUnbinding()) + BoundExprs.insert(MSE->opaquevalues_begin(), MSE->opaquevalues_end()); + return MSE->getWrappedExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, + Ctx, BoundExprs); + } case ImplicitCastExprClass: { const CastExpr *ICE = cast(this); @@ -2949,14 +3221,17 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, ICE->getSubExpr()->getType().isVolatileQualified()) return false; - return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case CXXDefaultArgExprClass: return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)); case CXXDefaultInitExprClass: return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -2966,13 +3241,13 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case MaterializeTemporaryExprClass: return cast(this) ->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case CXXBindTemporaryExprClass: return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case ExprWithCleanupsClass: return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); } } @@ -3048,32 +3323,65 @@ QualType Expr::findBoundMemberType(const Expr *expr) { } Expr *Expr::IgnoreImpCasts() { - return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreCasts() { - return IgnoreExprNodes(this, IgnoreCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreCastsSingleStep, IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreImplicit() { - return IgnoreExprNodes(this, IgnoreImplicitSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreImplicitAsWritten() { - return IgnoreExprNodes(this, IgnoreImplicitAsWrittenSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitAsWrittenSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreParens() { return IgnoreExprNodes(this, IgnoreParensSingleStep); } -Expr *Expr::IgnoreParenImpCasts() { +Expr *Expr::IgnoreParenImpCasts(llvm::SmallPtrSetImpl &BoundValues) { + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreImplicitCastsExtraSingleStep); + IgnoreImplicitCastsExtraSingleStep, + IgnoreBoundsSafetyImplicit); +} + +Expr *Expr::IgnoreParenImpCasts() { + llvm::SmallPtrSet BoundValues; + return IgnoreParenImpCasts(BoundValues); } Expr *Expr::IgnoreParenCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreConversionOperatorSingleStep() { @@ -3085,8 +3393,13 @@ Expr *Expr::IgnoreConversionOperatorSingleStep() { } Expr *Expr::IgnoreParenLValueCasts() { + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreLValueCastsSingleStep); + IgnoreLValueCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreParenBaseCasts() { @@ -3121,6 +3434,10 @@ Expr *Expr::IgnoreParenNoopCasts(const ASTContext &Ctx) { } Expr *Expr::IgnoreUnlessSpelledInSource() { + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; auto IgnoreImplicitConstructorSingleStep = [](Expr *E) { if (auto *Cast = dyn_cast(E)) { auto *SE = Cast->getSubExpr(); @@ -3159,7 +3476,7 @@ Expr *Expr::IgnoreUnlessSpelledInSource() { return IgnoreExprNodes( this, IgnoreImplicitSingleStep, IgnoreImplicitCastsExtraSingleStep, IgnoreParensOnlySingleStep, IgnoreImplicitConstructorSingleStep, - IgnoreImplicitMemberCallSingleStep); + IgnoreImplicitMemberCallSingleStep, IgnoreBoundsSafetyImplicit); } bool Expr::isDefaultArgument() const { @@ -3295,6 +3612,17 @@ bool Expr::hasAnyTypeDependentArguments(ArrayRef Exprs) { return false; } +namespace { +const ValueDecl *getUnderlyingDecl(ASTContext &Ctx, const Expr *E) { + E = E->IgnoreParenCasts(); + if (const auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (const auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + return nullptr; +} +} // namespace + bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, const Expr **Culprit) const { assert(!isValueDependent() && @@ -3364,6 +3692,16 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return DIUE->getBase()->isConstantInitializer(Ctx, false, Culprit) && DIUE->getUpdater()->isConstantInitializer(Ctx, false, Culprit); } + case ForgePtrExprClass: { + const ForgePtrExpr *FPE = cast(this); + if (!FPE->getAddr()->isConstantInitializer(Ctx, false, Culprit)) + return false; + if (FPE->ForgesBidiIndexablePointer()) + return FPE->getSize()->isConstantInitializer(Ctx, false, Culprit); + if (FPE->ForgesTerminatedByPointer()) + assert(FPE->getTerminator()->isConstantInitializer(Ctx, false, Culprit)); + return true; + } case InitListExprClass: { // C++ [dcl.init.aggr]p2: // The elements of an aggregate are: @@ -3418,6 +3756,32 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return false; } } else { + // BoundsSafety : Check if a static initializer for RecordType with + // dynamic count fields can be statically evaluated without runtime checks. + if (Field->getType()->isCountAttributedType()) { + auto Dcl = getUnderlyingDecl(Ctx, Elt); + if (Dcl && Dcl->getType()->isConstantArrayType()) { + if (Ctx.getTypeSizeInCharsIfKnown(Dcl->getType()).has_value()) { + // Strip the pointer cast and let it fall through. + Elt = Elt->IgnoreParens(); + if (auto ICE = dyn_cast(Elt)) { + if (ICE->getCastKind() == CK_BoundsSafetyPointerCast) + Elt = ICE->getSubExpr(); + } else if (auto FPPE = + dyn_cast(Elt)) { + Elt = FPPE->getPointer(); + } + } + } + } + + // FIXME: rdar://81135826 + if (Field->getType()->isDynamicRangePointerType()) { + if (Culprit) + *Culprit = Elt; + return false; + } + bool RefType = Field->getType()->isReferenceType(); if (!Elt->isConstantInitializer(Ctx, RefType, Culprit)) return false; @@ -3710,6 +4074,15 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ShuffleVectorExprClass: case ConvertVectorExprClass: case AsTypeExprClass: + case BoundsSafetyPointerPromotionExprClass: + case AssumptionExprClass: + case ForgePtrExprClass: + case GetBoundExprClass: + case PredefinedBoundsCheckExprClass: + case BoundsCheckExprClass: + case MaterializeSequenceExprClass: + case TerminatedByToIndexableExprClass: + case TerminatedByFromIndexableExprClass: case CXXParenListInitExprClass: // These have a side-effect if any subexpression does. break; @@ -3932,6 +4305,30 @@ bool Expr::hasNonTrivialCall(const ASTContext &Ctx) const { return Finder.hasNonTrivialCall(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +Expr::NullPointerConstantKind Expr::isNullPointerConstantIgnoreCastsAndOVEs( + ASTContext &Ctx, NullPointerConstantValueDependence NPC) const { + // Expr::isNullPointerConstant only handles explict `(void*)` casts so it + // won't detect something like `(int*)0` as a null pointer constant. Try to + // compensate for that here. + + const Expr *EPrevious = nullptr; + const Expr *ECurrent = this; + while (EPrevious != ECurrent) { + EPrevious = ECurrent; + + // Walk through parens and implicit/explicit casts + ECurrent = ECurrent->IgnoreParenCasts(); + + // Walk through OVEs + if (const auto *OVE = dyn_cast(ECurrent)) { + ECurrent = OVE->getSourceExpr(); + } + } + return ECurrent->isNullPointerConstant(Ctx, NPC); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// isNullPointerConstant - C99 6.3.2.3p3 - Return whether this is a null /// pointer constant or not, as well as the specific kind of constant detected. /// Null pointer constants can be integer constant expressions with the @@ -4870,6 +5267,24 @@ UnaryOperator *UnaryOperator::Create(const ASTContext &C, Expr *input, UnaryOperator(C, input, opc, type, VK, OK, l, CanOverflow, FPFeatures); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +OpaqueValueExpr *OpaqueValueExpr::Wrap(const ASTContext &Context, Expr *E) { + assert(!isa(E)); + return new (Context) OpaqueValueExpr( + E->getExprLoc(), E->getType(), E->getValueKind(), E->getObjectKind(), E); +} + +OpaqueValueExpr *OpaqueValueExpr::EnsureWrapped( + const ASTContext &Ctx, Expr *E, SmallVectorImpl &OVEs) { + auto *OVE = dyn_cast(E); + if (!OVE) { + OVE = Wrap(Ctx, E); + OVEs.push_back(OVE); + } + return OVE; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { if (const ExprWithCleanups *ewc = dyn_cast(e)) e = ewc->getSubExpr(); diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 6482cb6d39acc..85df6ff8f305c 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -202,8 +202,16 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: return Cl::CL_PRValue; + case Expr::AssumptionExprClass: + return ClassifyInternal(Ctx, cast(E)->getWrappedExpr()); + case Expr::EmbedExprClass: // Nominally, this just goes through as a PRValue until we actually expand // it and check it. @@ -257,6 +265,18 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { ? Cl::CL_PRValue : Cl::CL_LValue; return ClassifyDecl(Ctx, cast(E)->getDecl()); + case Expr::BoundsCheckExprClass: + // Simply look through. + return ClassifyInternal(Ctx, cast(E)->getGuardedExpr()); + + case Expr::PredefinedBoundsCheckExprClass: + // Simply look through. + return ClassifyInternal(Ctx, cast(E)->getGuardedExpr()); + + // Simply look through. + case Expr::MaterializeSequenceExprClass: + return ClassifyInternal(Ctx, cast(E)->getWrappedExpr()); + // Member access is complex. case Expr::MemberExprClass: return ClassifyMemberExpr(Ctx, cast(E)); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index fa7c83f8ee2a3..e61497cf14888 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -75,6 +75,11 @@ using llvm::APSInt; using llvm::APFloat; using llvm::FixedPointSemantics; +/* TO_UPSTREAM(BoundsSafety) ON */ +static uint64_t evaluateIncompleteArraySize(const ASTContext &C, + QualType T); +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { struct LValue; class CallStackFrame; @@ -181,6 +186,13 @@ namespace { llvm_unreachable("unknown ConstantExprKind"); } + /* TO_UPSTREAM(BoundsSafety) ON */ + static CharUnits getTypeSizeWithUnknownAsOne(const ASTContext &Ctx, + QualType Ty) { + return Ctx.getTypeSizeInCharsIfKnown(Ty).value_or(CharUnits::One()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// The bound to claim that an array of unknown bound has. /// The value in MostDerivedArraySize is undefined in this case. So, set it /// to an arbitrary value that's likely to loudly break things if it's used. @@ -212,6 +224,10 @@ namespace { if (auto *CAT = dyn_cast(AT)) { ArraySize = CAT->getZExtSize(); + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (auto NumElts = evaluateIncompleteArraySize(Ctx, Type)) { + ArraySize = NumElts; + /* TO_UPSTREAM(BoundsSafety) OFF */ } else { assert(I == 0 && "unexpected unsized array designator"); FirstEntryIsUnsizedArray = true; @@ -443,6 +459,35 @@ namespace { MostDerivedArraySize = 2; MostDerivedPathLength = Entries.size(); } + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Adjust type of forged pointer and scale up/down the array size and index. + void adjustForgedObjectTypeAndSize(QualType NewElemTy, + uint64_t OldElemSize, uint64_t NewElemSize) { + if (Invalid) + return; + assert(MostDerivedIsArrayElement && + MostDerivedPathLength == Entries.size()); + MostDerivedType = NewElemTy; + if (OldElemSize == NewElemSize) + return; + + const unsigned numBits = sizeof(uint64_t) * CHAR_BIT; + APInt APArraySize(numBits, MostDerivedArraySize); + APInt APOldElemSize(numBits, OldElemSize); + bool Overflow = false; + APArraySize = APArraySize.umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + assert(NewElemSize != 0); + MostDerivedArraySize = APArraySize.getZExtValue() / NewElemSize; + + APInt APArrayIndex(numBits, Entries.back().getAsArrayIndex()); + APArrayIndex = APArrayIndex.umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + uint64_t ArrayIndex = APArrayIndex.getZExtValue() / NewElemSize; + Entries.back() = PathEntry::ArrayIndex(ArrayIndex); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E); void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, const APSInt &N); @@ -1609,8 +1654,13 @@ namespace { APValue::LValueBase Base; CharUnits Offset; SubobjectDesignator Designator; + CharUnits ForgedSize; + CharUnits ForgedOffset; bool IsNullPtr : 1; bool InvalidBase : 1; + bool IsForgeBidi : 1; + bool IsForgeSingle : 1; + bool IsForgeTerminatedBy : 1; const APValue::LValueBase getLValueBase() const { return Base; } CharUnits &getLValueOffset() { return Offset; } @@ -1618,6 +1668,18 @@ namespace { SubobjectDesignator &getLValueDesignator() { return Designator; } const SubobjectDesignator &getLValueDesignator() const { return Designator;} bool isNullPointer() const { return IsNullPtr;} + bool isForgeBidi() const { return IsForgeBidi; } + bool isForgeSingle() const { return IsForgeSingle; } + bool isForgeTerminatedBy() const { return IsForgeTerminatedBy; } + CharUnits getLValueForgedSize() { return ForgedSize; } + const CharUnits &getLValueForgedSize() const { return ForgedSize; } + CharUnits getLValueForgedOffset() { return ForgedOffset; } + const CharUnits &getLValueForgedOffset() const { return ForgedOffset; } + CharUnits getUnwrappedLValueOffset() const { + if (isForgeBidi() || isForgeSingle() || isForgeTerminatedBy()) + return Offset + ForgedOffset; + return Offset; + } unsigned getLValueCallIndex() const { return Base.getCallIndex(); } unsigned getLValueVersion() const { return Base.getVersion(); } @@ -1630,6 +1692,12 @@ namespace { V = APValue(Base, Offset, Designator.Entries, Designator.IsOnePastTheEnd, IsNullPtr); } + if (IsForgeBidi) + V.setLValueForgedBidi(ForgedSize, ForgedOffset); + if (IsForgeSingle) + V.setLValueForgedSingle(ForgedOffset); + if (IsForgeTerminatedBy) + V.setLValueForgedTerminatedBy(ForgedOffset); } void setFrom(ASTContext &Ctx, const APValue &V) { assert(V.isLValue() && "Setting LValue from a non-LValue?"); @@ -1638,6 +1706,11 @@ namespace { InvalidBase = false; Designator = SubobjectDesignator(Ctx, V); IsNullPtr = V.isNullPointer(); + IsForgeBidi = V.isLValueForgeBidi(); + ForgedSize = V.getLValueForgedSize(); + ForgedOffset = V.getLValueForgedOffset(); + IsForgeSingle = V.isLValueForgeSingle(); + IsForgeTerminatedBy = V.isLValueForgeTerminatedBy(); } void set(APValue::LValueBase B, bool BInvalid = false) { @@ -1655,6 +1728,11 @@ namespace { InvalidBase = BInvalid; Designator = SubobjectDesignator(getType(B)); IsNullPtr = false; + IsForgeBidi = false; + IsForgeSingle = false; + IsForgeTerminatedBy = false; + ForgedSize = CharUnits::Zero(); + ForgedOffset = CharUnits::Zero(); } void setNull(ASTContext &Ctx, QualType PointerTy) { @@ -1664,6 +1742,25 @@ namespace { InvalidBase = false; Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; + IsForgeBidi = false; + IsForgeSingle = false; + IsForgeTerminatedBy = false; + ForgedSize = CharUnits::Zero(); + ForgedOffset = CharUnits::Zero(); + } + + void setForgedBidi(APValue::LValueBase B, QualType T, CharUnits Size, + CharUnits ForgedOfs, bool IsNullP) { + if (B.is() || B.is()) { + ForgedPtrLValue FP(B.getValueDecl()); + set(APValue::LValueBase::getForgedPtr(FP, T)); + } else { + Designator.setInvalid(); + } + IsForgeBidi = true; + ForgedSize = Size; + ForgedOffset = ForgedOfs; + IsNullPtr = IsNullP; } void setInvalid(APValue::LValueBase B, unsigned I = 0) { @@ -1713,6 +1810,38 @@ namespace { Designator.checkSubobject(Info, E, CSK); } + /* TO_UPSTREAM(BoundsSafety) ON */ + CharUnits getLValueSize(const ASTContext &Ctx) const { + if (IsForgeBidi) + return ForgedSize; + + if (IsForgeSingle || IsForgeTerminatedBy) { + QualType Ty = getType(Base); + // When forging a pointer from a constant there's no ValueDecl to + // provide an actual pointee type in consteval context. Since we cannot + // dereference a constant during consteval, the element size is + // effectively 0 (the only caller is validUpperAdjustment()). + if (!Ty.isNull()) + return getTypeSizeWithUnknownAsOne( + Ctx, QualType(Ty->getPointeeOrArrayElementType(), 0)); + } + + if (const auto *VD = Base.dyn_cast()) { + assert(!VD->getType()->isVoidType()); + return Ctx.getTypeSizeInChars(VD->getType()); + } + return CharUnits::Zero(); + } + + // Return the remaining lvalue object size. This is similar to + // SubobjectDesignator::validIndexAdjustments() but we can't always + // rely on it because SubobjectDesignator is easy to be invalid + // (due to bitcast, etc.). LValueOffset is more reliable. + CharUnits validUpperAdjustment(const ASTContext &Ctx) const { + return getLValueSize(Ctx) - getLValueOffset(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void addDecl(EvalInfo &Info, const Expr *E, const Decl *D, bool Virtual = false) { if (checkSubobject(Info, E, isa(D) ? CSK_Field : CSK_Base)) @@ -1738,6 +1867,28 @@ namespace { if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real)) Designator.addComplexUnchecked(EltTy, Imag); } + void adjustForgedObjectTypeAndSize(const ASTContext &Ctx, + QualType NewElemTy) { + ForgedPtrLValue FP = Base.get(); + const auto *CAT = + Ctx.getAsConstantArrayType(Base.getForgedPtrAsArrayType()); + assert(CAT); + QualType OldElemTy = CAT->getElementType(); + uint64_t OldElemSize = + getTypeSizeWithUnknownAsOne(Ctx, OldElemTy).getQuantity(); + uint64_t NewElemSize = + getTypeSizeWithUnknownAsOne(Ctx, NewElemTy).getQuantity(); + + bool Overflow = false; + APInt APOldElemSize(CAT->getSize().getBitWidth(), OldElemSize); + APInt APNewArraySize = CAT->getSize().umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + APNewArraySize = APNewArraySize.udiv(NewElemSize); + Base = APValue::LValueBase::getForgedPtr( + FP, Ctx.getConstantArrayType(NewElemTy, APNewArraySize, nullptr, + ArraySizeModifier::Normal, 0)); + Designator.adjustForgedObjectTypeAndSize(NewElemTy, OldElemSize, NewElemSize); + } void clearIsNullPointer() { IsNullPtr = false; } @@ -1888,6 +2039,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result, EvalInfo &Info, std::string *StringResult = nullptr); +static bool EvaluateTerminatorElement(const Expr *E, APValue &Result, + EvalInfo &Info); /// Evaluate an integer or fixed point expression into an APResult. static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, @@ -1911,6 +2064,28 @@ static void negateAsSigned(APSInt &Int) { Int = -Int; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static uint64_t evaluateIncompleteArraySize(const ASTContext &C, QualType T) { + if (!C.getLangOpts().BoundsSafety) + return 0; + + const auto *DCPT = T->getAs(); + if (!T->isIncompleteArrayType() || !DCPT) + return 0; + + APSInt Result; + Expr::EvalStatus Status; + EvalInfo Info(C, Status, EvalInfo::EM_ConstantExpression); + if (!EvaluateInteger(DCPT->getCountExpr(), Result, Info)) + return 0; + + if (Result.isNegative() || Result.ugt(std::numeric_limits::max())) + return 0; + + return Result.getLimitedValue(); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + template APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, ScopeKind Scope, LValue &LV) { @@ -2057,7 +2232,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { if (!B) return true; - if (const ValueDecl *D = B.dyn_cast()) { + // TO_UPSTREAM(BoundsSafety): getValueDecl + if (const ValueDecl *D = B.getValueDecl()) { // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast(D)) return VD->hasGlobalStorage(); @@ -7811,6 +7987,30 @@ class ExprEvaluatorBase return static_cast(this)->VisitCastExpr(E); } + bool VisitForgePtrExpr(const ForgePtrExpr *E) { + if (!StmtVisitorTy::Visit(E->getAddr())) + return false; + + if (E->ForgesBidiIndexablePointer()) + return StmtVisitorTy::Visit(E->getSize()); + if (E->ForgesTerminatedByPointer()) + assert(StmtVisitorTy::Visit(E->getTerminator())); + return true; + } + + bool VisitAssumptionExpr(const AssumptionExpr *E) { + return StmtVisitorTy::Visit(E->getWrappedExpr()); + } + + bool VisitGetBoundExpr(const GetBoundExpr *E) { + return static_cast(this)->Visit(E->getSubExpr()); + } + + bool VisitBoundsCheckExpr(const BoundsCheckExpr *E) { + // FIXME: C++ support + return false; + } + bool VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { default: @@ -7838,6 +8038,13 @@ class ExprEvaluatorBase } bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // The same binary conditional operator might occur multiple times in one + // bounds check expression. Don't create a temporary if it already exists. + if (Info.CurrentCall->getCurrentTemporary(E->getOpaqueValue())) + return HandleConditionalOperator(E); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Evaluate and cache the common expression. We treat it as a temporary, // even though it's not quite the same thing. LValue CommonLV; @@ -9096,6 +9303,8 @@ class PointerExprEvaluator bool VisitBinaryOperator(const BinaryOperator *E); bool VisitCastExpr(const CastExpr* E); bool VisitUnaryAddrOf(const UnaryOperator *E); + bool VisitGetBoundExpr(const GetBoundExpr *E); + bool VisitForgePtrExpr(const ForgePtrExpr *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { @@ -9232,6 +9441,82 @@ bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { return evaluateLValue(E->getSubExpr(), Result); } +bool PointerExprEvaluator::VisitGetBoundExpr(const GetBoundExpr *E) { + bool Res = evaluatePointer(E->getSubExpr(), Result); + if (!Res) + return false; + + auto &SubObj = Result.getLValueDesignator(); + auto maxAdjustments = SubObj.validIndexAdjustments(); + if (maxAdjustments.first == 0 && maxAdjustments.second == 0) { + Info.CCEDiag(E, diag::err_bounds_safety_evaluate_no_bounds) + << (int)E->getBoundKind(); + return false; + } + + APSInt Adjustment(64, true); // defaults to 0 + if (E->getBoundKind() == GetBoundExpr::BK_Lower) + (APInt &)Adjustment -= maxAdjustments.first; + else + (APInt &)Adjustment += maxAdjustments.second; + + // `E->getType()->getPointeeType() != MostDerivedType` when it's bitcast to + // `void *` and `Offset` must be calculated based on `MostDerivedType` of + // `LValueDesignator`. + return HandleLValueArrayAdjustment( + Info, E, Result, SubObj.MostDerivedType, + Adjustment); +} + +bool PointerExprEvaluator::VisitForgePtrExpr(const ForgePtrExpr *E) { + const ASTContext &Ctx = Info.Ctx; + const Expr *Addr = E->getAddr(); + if (Addr->getType()->isPointerType()) { + if (!evaluatePointer(Addr, Result)) + return false; + } else { + APSInt Value; + if (!EvaluateInteger(Addr, Value, Info)) + return false; + + unsigned TypeSize = Ctx.getTypeSize(E->getType()); + uint64_t Offset = Value.extOrTrunc(TypeSize).getZExtValue(); + Result.Base = (const ValueDecl *)nullptr; + Result.InvalidBase = false; + Result.Offset = CharUnits::fromQuantity(Offset); + Result.Designator.setInvalid(); + Result.IsNullPtr = Value.isZero(); + } + + if (E->ForgesBidiIndexablePointer()) { + APSInt Size; + if (!EvaluateInteger(E->getSize(), Size, Info)) + return false; + + QualType T = Ctx.getConstantArrayType(Ctx.UnsignedCharTy, Size, nullptr, + ArraySizeModifier::Normal, 0); + + // We keep the base offset since the forged pointer starts from there, yet + // the designator index shall start from 0. + // Pretend the value isn't NULL when the size isn't 0. This allows various + // constant operations to work on a forged NULL pointer. + auto CUSize = CharUnits::fromQuantity(Size.getZExtValue()); + Result.setForgedBidi(Result.getLValueBase(), T, CUSize, + Result.getUnwrappedLValueOffset(), + Result.IsNullPtr && CUSize.isZero()); + Result.addArray(Info, E, cast(T)); + } else { + assert(E->ForgesSinglePointer() != E->ForgesTerminatedByPointer()); + Result.IsForgeBidi = false; + Result.IsForgeSingle = E->ForgesSinglePointer(); + Result.IsForgeTerminatedBy = E->ForgesTerminatedByPointer(); + Result.ForgedOffset = Result.getUnwrappedLValueOffset(); + Result.Offset = CharUnits::Zero(); + } + + return true; +} + // Is the provided decl 'std::source_location::current'? static bool IsDeclSourceLocationCurrent(const FunctionDecl *FD) { if (!FD) @@ -9261,10 +9546,24 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_AddressSpaceConversion: if (!Visit(SubExpr)) return false; - // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are - // permitted in constant expressions in C++11. Bitcasts from cv void* are - // also static_casts, but we disallow them as a resolution to DR1312. - if (!E->getType()->isVoidPointerType()) { + + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: __builtin_unsafe_forge_* always returns void *, leading to + // a bitcast from void* to the assignee type. We special case such + // bitcasts so it doesn't invalidate the SubobjectDesignator. + if (!E->getType()->isVoidPointerType() && E->getCastKind() == CK_BitCast && + !Result.InvalidBase && !Result.Designator.Invalid && + (Result.IsForgeSingle || Result.IsForgeTerminatedBy || + Result.IsForgeBidi) && + SubExpr->getType()->isVoidPointerType()) { + if (Result.IsForgeBidi) + Result.adjustForgedObjectTypeAndSize(Info.Ctx, + E->getType()->getPointeeType()); + /* TO_UPSTREAM(BoundsSafety) OFF */ + } else if (!E->getType()->isVoidPointerType()) { + // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are + // permitted in constant expressions in C++11. Bitcasts from cv void* are + // also static_casts, but we disallow them as a resolution to DR1312. // In some circumstances, we permit casting from void* to cv1 T*, when the // actual pointee object is actually a cv2 T. bool HasValidResult = !Result.InvalidBase && !Result.Designator.Invalid && @@ -9303,6 +9602,22 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { Result.Designator.setInvalid(); } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: Conversion from 'void *__single' to 'T *__single' is allowed + // in the bounds-only profile. + // FIXME: check '__counted_by' and friends. + if (E->getCastKind() == CK_BitCast && E->getType()->isSinglePointerType() && + SubExpr->getType()->isSinglePointerType() && + !SubExpr->getType()->getPointeeType()->isIncompleteOrSizelessType()) { + auto DstElemSize = + Info.Ctx.getTypeSizeInChars(E->getType()->getPointeeType()); + auto SrcElemSize = + Info.Ctx.getTypeSizeInChars(SubExpr->getType()->getPointeeType()); + if (SrcElemSize < DstElemSize) + return false; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) ZeroInitialization(E); return true; @@ -9367,6 +9682,98 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { } } + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: { + // XXX: instead BoundsSafetyPointerCast doing heroics, we should constant- + // evaluate BoundsCheckExpr and fail if the bounds check fails or isn't + // constant. + + // Conversion rules. YES: always converts, CHECK: converts if static check + // passes, NO: never converts + // + // To | Bidi | Indexable | Single | Counted | Unsafe + // From -----+------+-----------+--------+---------+------- + // ----------+------+-----------+--------+---------+------- + // Bidi | YES | CHECK | CHECK | CHECK | YES + // Indexable | YES | YES | CHECK | CHECK | YES + // Single | YES | YES | CHECK | CHECK | YES + // Unsafe | NO | NO | NO | NO | YES + // + // In other words: "to unsafe" always works; else "from unsafe" never works; + // else "to counted" needs a check; else it works. ("To single" is a special + // case of "to counted" where the count is 1.) + // There never is a case where you convert from a counted pointer to + // something else because no lvalue turns into a counted pointer. + + auto ResultPtrTy = E->getType()->getAs(); + auto ResultFA = ResultPtrTy->getPointerAttributes(); + + // Evaluate the sub-expression and bail out if that didn't work. We do + // this first because NULL pointers are always valid constants. + if (!evaluatePointer(E->getSubExpr(), Result)) + return false; + + auto &SubObj = Result.getLValueDesignator(); + // "from unsafe or unspecified" only works if the destination is also + // unsafe; we can bail out early if that's not the case + auto InputPtrTy = E->getSubExpr()->getType()->getAs(); + auto InputFA = InputPtrTy->getPointerAttributes(); + auto InputIsUnsafe = InputFA.isUnsafeOrUnspecified() && !Result.IsNullPtr; + if (InputIsUnsafe && !ResultFA.isUnsafeOrUnspecified()) + return false; + + // "to unsafe" and "to bidi indexable" require no other checks. + if (ResultFA.isUnsafeOrUnspecified() || ResultFA.isBidiIndexable()) + return true; + + // "to indexable" requires that the pointer value is not less than the lower + // bound. Since a pointer's value cannot be reduced, a pointer above its + // upper bound is useless. This gives us a good reason to reject it, which + // makes the test easy: if the sub-object is invalid, prevent constant + // evaluation. + if (ResultFA.isIndexable()) { + return Result.IsNullPtr || SubObj.isValidSubobject(); + } + + if (E->getType()->isDynamicRangePointerType()) { + // rdar://81135826 + return false; + } + + // Otherwise, this is a __counted_by pointer or a plain __single pointer. + // A __single pointer tolerates NULL as a sentinel value; otherwise, it's + // the same as a __counted_by pointer with a length of 1. + APSInt ItemCount; + if (auto DCP = E->getType()->getAs()) { + if (DCP->isOrNull() && Result.IsNullPtr) + return true; + if (!EvaluateInteger(DCP->getCountExpr(), ItemCount, Info)) { + return false; + } + } else { + // Plain __single pointer. NULL always works. + if (Result.IsNullPtr) + return true; + + QualType PointeeTy = ResultPtrTy->getPointeeType(); + + if (PointeeTy->isIncompleteType()) + return true; + + // To zero-sized type also always works. + auto ObjSize = Info.Ctx.getTypeSizeInChars(PointeeTy); + if (ObjSize.getQuantity() == 0) + return true; + + ItemCount = APInt(64, 1); + } + + // ensure that there is room for at least that many elements + auto MaxAdjustment = SubObj.validIndexAdjustments().second; + return ItemCount <= MaxAdjustment; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_ArrayToPointerDecay: { if (SubExpr->isGLValue()) { if (!evaluateLValue(SubExpr, Result)) @@ -9379,9 +9786,15 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { } // The result is a pointer to the first element of the array. auto *AT = Info.Ctx.getAsArrayType(SubExpr->getType()); - if (auto *CAT = dyn_cast(AT)) + if (auto *CAT = dyn_cast(AT)) { Result.addArray(Info, E, CAT); - else + } else if (uint64_t IATEltCount = evaluateIncompleteArraySize(Info.Ctx, SubExpr->getType())) { + llvm::APInt EltCount(64, IATEltCount); + QualType CAT = + Info.Ctx.getConstantArrayType(AT->getElementType(), EltCount, nullptr, + ArraySizeModifier::Normal, 0); + Result.addArray(Info, E, cast(CAT)); + } else Result.addUnsizedArray(Info, E, AT->getElementType()); return true; } @@ -12038,6 +12451,8 @@ static QualType getObjectType(APValue::LValueBase B) { return B.getTypeInfoType(); } else if (B.is()) { return B.getDynamicAllocType(); + } else if (B.is()) { + return B.getForgedPtrAsArrayType(); } return QualType(); @@ -14290,6 +14705,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_FixedPointCast: case CK_IntegralToFixedPoint: case CK_MatrixCast: + case CK_BoundsSafetyPointerCast: case CK_HLSLVectorTruncation: llvm_unreachable("invalid cast kind for integral value"); @@ -14431,7 +14847,13 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { // FIXME: Allow a larger integer size than the pointer size, and allow // narrowing back down to pointer width in subsequent integral casts. // FIXME: Check integer type's active bits, not its type size. - if (Info.Ctx.getTypeSize(DestType) != Info.Ctx.getTypeSize(SrcType)) + if (Info.Ctx.getTypeSize(DestType) != Info.Ctx.getTypeSize(SrcType) + /* TO_UPSTREAM(BoundsSafety) ON */ + && (!SrcType->isPointerTypeWithBounds() || + Info.Ctx.getTypeSize(DestType) != + Info.Ctx.getTypeSize(Info.Ctx.getIntPtrType())) + /* TO_UPSTREAM(BoundsSafety) OFF */ + ) return Error(E); LV.Designator.setInvalid(); @@ -15130,6 +15552,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_FixedPointToIntegral: case CK_IntegralToFixedPoint: case CK_MatrixCast: + case CK_BoundsSafetyPointerCast: case CK_HLSLVectorTruncation: llvm_unreachable("invalid cast kind for complex value"); @@ -16464,10 +16887,21 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CoawaitExprClass: case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); + case Expr::AssumptionExprClass: + return CheckICE(cast(E)->getWrappedExpr(), Ctx); + case Expr::InitListExprClass: { // C++03 [dcl.init]p13: If T is a scalar type, then a declaration of the // form "T x = { a };" is equivalent to "T x = a;". @@ -17157,3 +17591,104 @@ bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); return EvaluateBuiltinStrLen(this, Result, Info); } + +static bool +EvaluateTerminatorElementFromCondOpIgnoringCond(const Expr *E, APValue &Result, + const ASTContext &Ctx) { + const auto *CondOp = dyn_cast(E); + if (!CondOp) + return false; + + const auto *TrueE = CondOp->getTrueExpr(); + const auto *FalseE = CondOp->getFalseExpr(); + + Expr::EvalResult TrueRes; + if (!TrueE->tryEvaluateTerminatorElement(TrueRes, Ctx) || + !TrueRes.Val.isInt()) + return false; + + Expr::EvalResult FalseRes; + if (!FalseE->tryEvaluateTerminatorElement(FalseRes, Ctx) || + !FalseRes.Val.isInt()) + return false; + + if (!llvm::APSInt::isSameValue(TrueRes.Val.getInt(), FalseRes.Val.getInt())) + return false; + + Result = TrueRes.Val; + return true; +} + +static bool EvaluateTerminatorElement(const Expr *E, APValue &Result, + EvalInfo &Info) { + if (!E->getType()->hasPointerRepresentation()) + return false; + + QualType PointeeTy = E->getType()->getPointeeType(); + for (;;) { + E = E->IgnoreParens(); + const auto *ICE = dyn_cast(E); + if (!ICE) + break; + CastKind K = ICE->getCastKind(); + if (!(K == CK_BitCast || K == CK_BoundsSafetyPointerCast)) + break; + const auto *SubE = ICE->getSubExpr(); + if (!Info.Ctx.hasSameType(SubE->getType()->getPointeeType(), PointeeTy)) + break; + E = SubE; + } + + if (const auto *VTT = E->getType()->getAs()) { + Result = APValue(VTT->getTerminatorValue(Info.Ctx)); + return true; + } + + if (EvaluateTerminatorElementFromCondOpIgnoringCond(E, Result, Info.Ctx)) + return true; + + LValue Array; + if (!EvaluatePointer(E, Array, Info)) + return false; + + QualType EltTy = E->getType()->getPointeeType(); + CharUnits EltSz; + if (!HandleSizeof(Info, E->getExprLoc(), EltTy, EltSz)) + return false; + + size_t MaxIndex = Array.Designator.validIndexAdjustments().second; + if (MaxIndex == 0) + return false; + + size_t LastIndex = MaxIndex - 1; + if (!HandleLValueArrayAdjustment(Info, E, Array, EltTy, LastIndex)) + return false; + + return handleLValueToRValueConversion(Info, E, EltTy, Array, Result); +} + +bool Expr::tryEvaluateTerminatorElement(EvalResult &Result, + const ASTContext &Ctx) const { + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); + return EvaluateTerminatorElement(this, Result.Val, Info); +} + +bool Expr::EvaluateAsTerminatorValue( + llvm::APSInt &Result, const ASTContext &Ctx, + Expr::SideEffectsKind AllowSideEffects) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + + QualType Ty = getType(); + if (!Ty->isIntegralOrEnumerationType() && !Ty->isPointerType()) + return false; + + EvalResult ExprResult; + if (!EvaluateAsRValue(ExprResult, Ctx) || + hasUnacceptableSideEffect(ExprResult, AllowSideEffects)) { + return false; + } + + return ExprResult.Val.toIntegralConstant(Result, Ty, Ctx); +} diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 54ce607b11ee1..a32fbf8887afa 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2432,6 +2432,10 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::BitInt: case Type::DependentBitInt: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ llvm_unreachable("type is illegal as a nested name specifier"); case Type::SubstTemplateTypeParmPack: @@ -3744,6 +3748,7 @@ void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) { // ::= P # pointer-to void CXXNameMangler::mangleType(const PointerType *T) { + // FIXME: add a mangling rule for BoundsSafetyPointerKind. Out << 'P'; mangleType(T->getPointeeType()); } @@ -4770,7 +4775,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::SourceLocExprClass: case Expr::EmbedExprClass: case Expr::BuiltinBitCastExprClass: - { + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::AssumptionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: { NotPrimaryExpr(); if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 4016043df62ed..2240fc493fa5f 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3262,6 +3262,7 @@ void MicrosoftCXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T, // # the E is required for 64-bit non-static pointers void MicrosoftCXXNameMangler::mangleType(const PointerType *T, Qualifiers Quals, SourceRange Range) { + // FIXME: implement a mangling for BoundsSafetyPointerKind QualType PointeeType = T->getPointeeType(); manglePointerCVQualifiers(Quals); manglePointerExtQualifiers(Quals, PointeeType); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 69e0b763e8ddc..1428b689a23cb 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2799,6 +2799,84 @@ void StmtPrinter::VisitAsTypeExpr(AsTypeExpr *Node) { OS << ")"; } +void StmtPrinter::VisitGetBoundExpr(GetBoundExpr *Node) { + OS << "__builtin_get_pointer_"; + OS << (Node->getBoundKind() == GetBoundExpr::BK_Lower ? "lower" : "upper"); + OS << "_bound("; + PrintExpr(Node->getSubExpr()); + OS << ")"; +} + +void StmtPrinter::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *Node) { + PrintExpr(Node->getPointer()); +} + +void StmtPrinter::VisitAssumptionExpr(AssumptionExpr *E) { + PrintExpr(E->getWrappedExpr()); +} + +void StmtPrinter::VisitForgePtrExpr(ForgePtrExpr *Node) { + if (Node->ForgesBidiIndexablePointer()) { + OS << "__builtin_unsafe_forge_bidi_indexable("; + PrintExpr(Node->getAddr()); + OS << ", "; + PrintExpr(Node->getSize()); + OS << ")"; + } else if (Node->ForgesTerminatedByPointer()) { + OS << "__builtin_unsafe_forge_terminated_by("; + PrintExpr(Node->getAddr()); + OS << ", "; + PrintExpr(Node->getTerminator()); + OS << ")"; + } else { + OS << "__builtin_unsafe_forge_single("; + PrintExpr(Node->getAddr()); + OS << ")"; + } +} + +void StmtPrinter::VisitBoundsCheckExpr(BoundsCheckExpr *Node) { + PrintExpr(Node->getGuardedExpr()); +} + +void StmtPrinter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *Node) { + PrintExpr(Node->getGuardedExpr()); +} + +void StmtPrinter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *Node) { + PrintExpr(Node->getWrappedExpr()); +} + +void StmtPrinter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *Node) { + const char *Name = Node->includesTerminator() + ? "__builtin_unsafe_terminated_by_to_indexable" + : "__builtin_terminated_by_to_indexable"; + OS << Name << '('; + PrintExpr(Node->getPointer()); + if (auto *Terminator = Node->getTerminator()) { + OS << ", "; + PrintExpr(Terminator); + } + OS << ")"; +} + +void StmtPrinter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *Node) { + const auto *VTT = cast(Node->getType()); + OS << "__builtin_unsafe_terminated_by_from_indexable("; + PrintExpr(VTT->getTerminatorExpr()); + OS << ", "; + PrintExpr(Node->getPointer()); + if (auto *PointerToTerminator = Node->getPointerToTerminator()) { + OS << ", "; + PrintExpr(PointerToTerminator); + } + OS << ")"; +} + //===----------------------------------------------------------------------===// // Stmt method implementations //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 89d2a422509d8..ae975b37607f9 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2471,6 +2471,51 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { } } +/* TO_UPSTREAM(BoundsSafety) ON */ +void StmtProfiler::VisitBoundsSafetyPointerPromotionExpr( + const BoundsSafetyPointerPromotionExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitAssumptionExpr(const AssumptionExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitForgePtrExpr(const ForgePtrExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitGetBoundExpr(const GetBoundExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getBoundKind()); +} + +void StmtProfiler::VisitBoundsCheckExpr(const BoundsCheckExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitPredefinedBoundsCheckExpr( + const PredefinedBoundsCheckExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->isUnbinding()); +} + +void StmtProfiler::VisitTerminatedByToIndexableExpr( + const TerminatedByToIndexableExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->includesTerminator()); +} + +void StmtProfiler::VisitTerminatedByFromIndexableExpr( + const TerminatedByFromIndexableExpr *S) { + VisitExpr(S); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { class OpenACCClauseProfiler : public OpenACCClauseVisitor { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index a1e8b5864ba14..7e98dffde147a 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1775,6 +1775,26 @@ void TextNodeDumper::VisitOMPIteratorExpr(const OMPIteratorExpr *Node) { } } +void TextNodeDumper::VisitGetBoundExpr(const GetBoundExpr *Expr) { + OS << (Expr->getBoundKind() == GetBoundExpr::BK_Lower ? " lower" : " upper"); +} + +void TextNodeDumper::VisitMaterializeSequenceExpr( + const MaterializeSequenceExpr *Node) { + OS << (Node->isUnbinding() ? " " : " "); +} + +void TextNodeDumper::VisitPredefinedBoundsCheckExpr( + const PredefinedBoundsCheckExpr *Node) { + OS << " <" << Node->getKindName() << ">"; +} + +void TextNodeDumper::VisitBoundsCheckExpr(const BoundsCheckExpr *Node) { + OS << " '"; + Node->getCond()->printPretty(OS, nullptr, PrintPolicy, 0, "\n", Context); + OS << "'"; +} + void TextNodeDumper::VisitConceptSpecializationExpr( const ConceptSpecializationExpr *Node) { OS << " "; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 7dfbc71995659..47862d1a25ecb 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -421,6 +421,41 @@ void CountAttributedType::Profile(llvm::FoldingSetNodeID &ID, ID.AddPointer(CountExpr); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void DynamicRangePointerType::Profile(llvm::FoldingSetNodeID &ID, + QualType PointerTy, + Expr *StartPtr, + Expr *EndPtr, + unsigned NumStartDecls, + unsigned NumEndDecls) { + ID.AddPointer(PointerTy.getAsOpaquePtr()); + // We profile it as a pointer as the StmtProfiler considers parameter + // expressions on function declaration and function definition as the + // same, resulting in count expression being evaluated with ParamDecl + // not in the function scope. + ID.AddPointer(StartPtr); + ID.AddPointer(EndPtr); + ID.AddInteger(NumStartDecls); + ID.AddInteger(NumEndDecls); +} + +llvm::APSInt +ValueTerminatedType::getTerminatorValue(const ASTContext &Ctx) const { + llvm::APSInt Result; + bool Ok = TerminatorExpr->EvaluateAsTerminatorValue(Result, Ctx); + (void)Ok; + assert(Ok); + return Result; +} + +void ValueTerminatedType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Ctx, QualType OriginalTy, + const Expr *TerminatorExpr) { + ID.AddPointer(OriginalTy.getAsOpaquePtr()); + TerminatorExpr->Profile(ID, Ctx, true); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// getArrayElementTypeNoTypeQual - If this is an array type, return the /// element type of the array, potentially with type qualifiers missing. /// This method should never be used when type qualifiers are meaningful. @@ -599,6 +634,16 @@ template <> const CountAttributedType *Type::getAs() const { return getAsSugar(this); } +/* TO_UPSTREAM(BoundsSafety) ON */ +template <> const DynamicRangePointerType *Type::getAs() const { + return getAsSugar(this); +} + +template <> const ValueTerminatedType *Type::getAs() const { + return getAsSugar(this); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic /// sugar off the given type. This should produce an object of the /// same dynamic type as the canonical type. @@ -695,6 +740,66 @@ bool Type::isCountAttributedType() const { return getAs(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool Type::isAnyVaListType(ASTContext &Ctx) const { + bool MaybeMS = (Ctx.getTargetInfo().hasBuiltinMSVaList() && + Ctx.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList); + + const Type *VaListType = Ctx.getBuiltinVaListType().getTypePtr(); + const Type *MSVaListType = MaybeMS ? Ctx.getBuiltinMSVaListType().getTypePtr() : nullptr; + + auto isVaListTypeSingle = [&](const Type *T) -> bool { + return T == VaListType || (MaybeMS && T == MSVaListType); + }; + + const Type *Cur = this; + if (const auto *AT = dyn_cast(this)) { + Cur = AT->getOriginalType().getTypePtr(); + } + if (!Ctx.hasSameType(VaListType, Cur) && + MaybeMS && !Ctx.hasSameType(MSVaListType, Cur)) + return false; + + while (true) { + if (isVaListTypeSingle(Cur)) + return true; + switch (Cur->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Parent) +#define TYPE(Class, Parent) \ + case Type::Class: { \ + const auto *Ty = cast(Cur); \ + if (!Ty->isSugared()) \ + return 0; \ + Cur = Ty->desugar().getTypePtr(); \ + break; \ + } +#include "clang/AST/TypeNodes.inc" + } + } + return false; +} + +bool Type::isDynamicRangePointerType() const { + return getAs(); +} + +bool Type::isBoundsAttributedType() const { + return getAs(); +} + +bool Type::isValueTerminatedType() const { + return getAs(); +} + +bool Type::isImplicitlyNullTerminatedType(const ASTContext &Ctx) const { + const auto *VT = getAs(); + if (!VT || !VT->getTerminatorValue(Ctx).isZero()) + return false; + return hasAttr(attr::PtrAutoNullTerminatedAttr); +} + +/* TO_UPSTREAM(BoundsSafety) OFF */ + const ComplexType *Type::getAsComplexIntegerType() const { if (const auto *Complex = getAs()) if (Complex->getElementType()->isIntegerType()) @@ -2526,6 +2631,37 @@ bool Type::isSveVLSBuiltinType() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +bool Type::isSizeMeaningless() const { + if (isIncompleteType()) + return true; + if (isSizelessType()) + return true; + if (isFunctionType()) + return true; + if (auto *RT = getAs()) + if (RT->getDecl()->hasFlexibleArrayMember()) + return true; + return false; +} + +bool Type::isIncompletableIncompleteType() const { + if (!isIncompleteType()) + return false; + + // Forward declarations of structs, classes, enums, and unions could be later + // completed in a compilation unit by providing a definition. + if (isStructureOrClassType() || isEnumeralType() || isUnionType()) + return false; + + // Other types are incompletable. + // + // E.g. `char[]` and `void`. The type is incomplete and no future + // type declarations can make the type complete. + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + QualType Type::getSizelessVectorEltType(const ASTContext &Ctx) const { assert(isSizelessVectorType() && "Must be sizeless vector type"); // Currently supports SVE and RVV @@ -3816,12 +3952,19 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, getExtProtoInfo(), Ctx, isCanonicalUnqualified()); } -TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref) - : Data(D, Deref << DerefShift) {} +TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref, + // TO_UPSTREAM(BoundsSafety) + bool Member) + : Data(D, (Deref << DerefShift) | (Member << MemberShift)) {} +/* TO_UPSTREAM(BoundsSafety) ON*/ bool TypeCoupledDeclRefInfo::isDeref() const { - return Data.getInt() & DerefMask; + return (Data.getInt() >> DerefShift) & DerefMask; +} +bool TypeCoupledDeclRefInfo::isMember() const { + return (Data.getInt() >> MemberShift) & MemberMask; } +/* TO_UPSTREAM(BoundsSafety) OFF*/ ValueDecl *TypeCoupledDeclRefInfo::getDecl() const { return Data.getPointer(); } unsigned TypeCoupledDeclRefInfo::getInt() const { return Data.getInt(); } void *TypeCoupledDeclRefInfo::getOpaqueValue() const { @@ -3853,6 +3996,48 @@ CountAttributedType::CountAttributedType( DeclSlot[i] = CoupledDecls[i]; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +StringRef CountAttributedType::GetAttributeName(bool WithMacroPrefix) const { +#define ENUMERATE_ATTRS(PREFIX) \ + do { \ + if (isCountInBytes()) { \ + if (isOrNull()) \ + return PREFIX "sized_by_or_null"; \ + return PREFIX "sized_by"; \ + } \ + if (isOrNull()) \ + return PREFIX "counted_by_or_null"; \ + return PREFIX "counted_by"; \ + } while (0) + + if (WithMacroPrefix) + ENUMERATE_ATTRS("__"); + else + ENUMERATE_ATTRS(""); + +#undef ENUMERATE_ATTRS +} + +DynamicRangePointerType::DynamicRangePointerType( + QualType PointerTy, QualType CanPointerTy, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) + : BoundsAttributedType(DynamicRangePointer, PointerTy, CanPointerTy), + StartPtr(StartPtr), EndPtr(EndPtr) { + assert(EndPtrDecls.size() < (1 << 16)); + assert(StartPtrDecls.size() < (1 << 16)); + DynamicRangePointerTypeBits.NumEndPtrDecls = EndPtrDecls.size(); + DynamicRangePointerTypeBits.NumStartPtrDecls = StartPtrDecls.size(); + auto *DeclSlot = getTrailingObjects(); + Decls = llvm::ArrayRef(DeclSlot, + EndPtrDecls.size() + StartPtrDecls.size()); + for (unsigned i = 0; i != StartPtrDecls.size(); ++i) + *(DeclSlot++) = StartPtrDecls[i]; + for (unsigned i = 0; i != EndPtrDecls.size(); ++i) + *(DeclSlot++) = EndPtrDecls[i]; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType Underlying, QualType can) : Type(tc, can, toSemanticDependence(can->getDependence())), diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 33e6ccbadc12d..f0eb1dcca377c 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -523,6 +523,12 @@ SourceRange CountAttributedTypeLoc::getLocalSourceRange() const { return getCountExpr() ? getCountExpr()->getSourceRange() : SourceRange(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +SourceRange DynamicRangePointerTypeLoc::getLocalSourceRange() const { + return getEndPointer() ? getEndPointer()->getSourceRange() : SourceRange(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 8c29e29717b8a..05401e1db4597 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -111,6 +111,21 @@ class ElaboratedTypePolicyRAII { } }; +class DelayedArrayQualPolicyRAII { + PrintingPolicy &Policy; + std::string DelayedArrayQual; + +public: + explicit DelayedArrayQualPolicyRAII(PrintingPolicy &Policy, const std::string &Qual) : Policy(Policy) { + DelayedArrayQual = Policy.DelayedArrayQual; + Policy.DelayedArrayQual = Qual; + } + + ~DelayedArrayQualPolicyRAII() { + Policy.DelayedArrayQual = std::move(DelayedArrayQual); + } +}; + class TypePrinter { PrintingPolicy Policy; unsigned Indentation; @@ -290,6 +305,10 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ CanPrefixQualifiers = false; break; @@ -403,15 +422,30 @@ void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) { printAfter(T->getElementType(), OS); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool shouldPrintParenForPointer(const PointerType *T) { + QualType PointeeTy = T->getPointeeType(); + if (auto *DCPTy = PointeeTy->getAs()) + PointeeTy = DCPTy->desugar(); + + return isa(PointeeTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printBefore(T->getPointeeType(), OS); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. - if (isa(T->getPointeeType())) + /* TO_UPSTREAM(BoundsSafety) ON */ + // It checks `isa(T->getPointeeType())` in upstream. + if (shouldPrintParenForPointer(T)) + /* TO_UPSTREAM(BoundsSafety) OFF */ OS << '('; OS << '*'; + + T->getPointerAttributes().print(OS); } void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { @@ -419,7 +453,10 @@ void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. - if (isa(T->getPointeeType())) + /* TO_UPSTREAM(BoundsSafety) ON */ + // It checks `isa(T->getPointeeType())` in upstream. + if (shouldPrintParenForPointer(T)) + /* TO_UPSTREAM(BoundsSafety) OFF */ OS << ')'; printAfter(T->getPointeeType(), OS); } @@ -553,7 +590,7 @@ void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T, void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T, raw_ostream &OS) { - OS << "[]"; + OS << '[' << Policy.DelayedArrayQual << ']'; printAfter(T->getElementType(), OS); } @@ -1195,6 +1232,14 @@ void TypePrinter::printUsingBefore(const UsingType *T, raw_ostream &OS) { void TypePrinter::printUsingAfter(const UsingType *T, raw_ostream &OS) {} void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (StringRef MaybeBoundsSafetyName = T->getDecl()->getName(); + MaybeBoundsSafetyName.starts_with("__bounds_safety")) { + this->print(T->desugar(), OS, ""); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + printTypeSpec(T->getDecl(), OS); } @@ -1787,7 +1832,13 @@ void TypePrinter::printPackExpansionAfter(const PackExpansionType *T, static void printCountAttributedImpl(const CountAttributedType *T, raw_ostream &OS, const PrintingPolicy &Policy) { - OS << ' '; + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: The upstream version doesn't have this conditional check for adding + // a leading space and instead always adds it. + if (!T->isArrayType() || !Policy.CountedByInArrayBracket) + OS << ' '; + /* TO_UPSTREAM(BoundsSafety) OFF */ + if (T->isCountInBytes() && T->isOrNull()) OS << "__sized_by_or_null("; else if (T->isCountInBytes()) @@ -1810,11 +1861,85 @@ void TypePrinter::printCountAttributedBefore(const CountAttributedType *T, void TypePrinter::printCountAttributedAfter(const CountAttributedType *T, raw_ostream &OS) { + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Policy.CountedByInArrayBracket && T->isArrayType()) { + std::string DelayedQual; + llvm::raw_string_ostream DOS(DelayedQual); + printCountAttributedImpl(T, DOS, Policy); + DelayedArrayQualPolicyRAII DP(Policy, DOS.str()); + printAfter(T->desugar(), OS); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + printAfter(T->desugar(), OS); if (T->isArrayType()) printCountAttributedImpl(T, OS, Policy); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypePrinter::printDynamicRangePointerBefore( + const DynamicRangePointerType *T, raw_ostream &OS) { + printBefore(T->desugar(), OS); + const Expr *StartPtr = T->getStartPointer(); + const Expr *EndPtr = T->getEndPointer(); + if (EndPtr) { + OS << " __ended_by("; + EndPtr->printPretty(OS, nullptr, Policy); + OS << ')'; + } + + if (StartPtr) { + OS << " /* __started_by("; + StartPtr->printPretty(OS, nullptr, Policy); + OS << ") */ "; + } +} + +void TypePrinter::printDynamicRangePointerAfter( + const DynamicRangePointerType *T, raw_ostream &OS) { + printAfter(T->desugar(), OS); +} + +static void printTerminatedByAttr(const ValueTerminatedType *T, raw_ostream &OS, + const PrintingPolicy &Policy) { + OS << "__terminated_by("; + T->getTerminatorExpr()->printPretty(OS, nullptr, Policy); + OS << ')'; +} + +void TypePrinter::printValueTerminatedBefore(const ValueTerminatedType *T, + raw_ostream &OS) { + QualType DesugaredTy = T->desugar(); + assert(DesugaredTy->isArrayType() || DesugaredTy->isPointerType()); + printBefore(DesugaredTy, OS); + if (DesugaredTy->isPointerType()) { + OS << ' '; + printTerminatedByAttr(T, OS, Policy); + } +} + +void TypePrinter::printValueTerminatedAfter(const ValueTerminatedType *T, + raw_ostream &OS) { + QualType DesugaredTy = T->desugar(); + if (DesugaredTy->isPointerType()) { + printAfter(DesugaredTy, OS); + } else { + assert(DesugaredTy->isArrayType()); + std::string Str; + llvm::raw_string_ostream SS(Str); + printAfter(DesugaredTy, SS); + SS.flush(); + assert(Str.size() >= 2 && Str[0] == '['); + OS << '['; + printTerminatedByAttr(T, OS, Policy); + if (Str[1] != ']') + OS << ' '; + OS << Str.c_str() + 1; + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypePrinter::printAttributedBefore(const AttributedType *T, raw_ostream &OS) { // FIXME: Generate this with TableGen. @@ -1906,6 +2031,23 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, return; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Don't print implicit type attribute PtrAutoAttr + if (T->getAttrKind() == attr::PtrAutoAttr) + return; + if (T->getAttrKind() == attr::PtrAutoNullTerminatedAttr) + return; + + if (T->getAttrKind() == attr::PtrSingle) { + OS << "__single"; + return; + } + if (T->getAttrKind() == attr::PtrUnsafeIndexable) { + OS << "__unsafe_indexable"; + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // The printing of the address_space attribute is handled by the qualifier // since it is still stored in the qualifier. Return early to prevent printing // this twice. @@ -1961,6 +2103,19 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // AttributedType nodes for them. break; + /* TO_UPSTREAM(BoundsSafety) ON */ + case attr::ArrayDecayDiscardsCountInParameters: + OS << "decay_discards_count_in_parameters"; + break; + + case attr::PtrBidiIndexable: + case attr::PtrSingle: + case attr::PtrIndexable: + case attr::PtrUnsafeIndexable: + case attr::PtrEndedBy: + case attr::PtrTerminatedBy: + /* TO_UPSTREAM(BoundsSafety) OFF */ + case attr::CountedBy: case attr::CountedByOrNull: case attr::SizedBy: @@ -1971,6 +2126,8 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::TypeNullable: case attr::TypeNullableResult: case attr::TypeNullUnspecified: + case attr::PtrAutoAttr: + case attr::PtrAutoNullTerminatedAttr: case attr::ObjCGC: case attr::ObjCInertUnsafeUnretained: case attr::ObjCKindOf: diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 64e6155de090c..d0920c3b862de 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -628,6 +628,15 @@ class CFGBuilder { CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); CFGBlock *VisitWhileStmt(WhileStmt *W); CFGBlock *VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, AddStmtChoice asc); + /* TO_UPSTREAM(BoundsSafety) ON */ + CFGBlock *VisitBoundsCheckExpr(BoundsCheckExpr *BCE, AddStmtChoice asc); + CFGBlock *VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *BCE, + AddStmtChoice asc); + CFGBlock *VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE, + AddStmtChoice asc); + CFGBlock *VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E, + AddStmtChoice asc); + /* TO_UPSTREAM(BoundsSafety) OFF */ CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd, bool ExternallyDestructed = false); @@ -2365,6 +2374,23 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::OpaqueValueExprClass: return Block; + /* TO_UPSTREAM(BoundsSafety) ON */ + case Stmt::MaterializeSequenceExprClass: + return VisitMaterializeSequenceExpr(cast(S), + asc); + + case Stmt::BoundsCheckExprClass: + return VisitBoundsCheckExpr(cast(S), asc); + + case Stmt::PredefinedBoundsCheckExprClass: + return VisitPredefinedBoundsCheckExpr(cast(S), + asc); + + case Stmt::BoundsSafetyPointerPromotionExprClass: + return VisitBoundsSafetyPointerPromotionExpr( + cast(S), asc); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Stmt::PseudoObjectExprClass: return VisitPseudoObjectExpr(cast(S)); @@ -4914,6 +4940,48 @@ CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, return Visit(E->getSubExpr(), AddStmtChoice()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +CFGBlock *CFGBuilder::VisitBoundsCheckExpr(BoundsCheckExpr *E, + AddStmtChoice asc) { + CFGBlock *Ret = Visit(E->getGuardedExpr(), asc); + + for (Expr *subExpr : llvm::reverse(E->opaquevalues())) { + auto *ove = cast(subExpr); + Ret = Visit(ove->getSourceExpr(), asc); + } + return Ret; +} + +CFGBlock * +CFGBuilder::VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *E, + AddStmtChoice asc) { + return Visit(E->getGuardedExpr(), asc); +} + +CFGBlock *CFGBuilder::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E, + AddStmtChoice asc) { + CFGBlock *Ret = Visit(E->getWrappedExpr(), asc); + + if (E->isBinding()) { + for (Expr *subExpr : llvm::reverse(E->opaquevalues())) { + auto *ove = cast(subExpr); + Ret = Visit(ove->getSourceExpr(), asc); + } + } + return Ret; +} + +CFGBlock *CFGBuilder::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + return Visit(E->getSubExpr(), asc); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + CFGBlock *CFGBuilder::VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc) { return Visit(E->getSubExpr(), AddStmtChoice()); } @@ -5811,6 +5879,10 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, } else if (const CastExpr *CE = dyn_cast(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() << ", " << CE->getType() << ")"; + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (const auto *PE = dyn_cast(S)) { + OS << " (" << PE->getStmtClassName() << ", " << PE->getType() << ")"; + /* TO_UPSTREAM(BoundsSafety) OFF */ } // Expressions need a newline. diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 018f4b99abd1d..1f391ccc5546a 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -11,6 +11,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/FormatString.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtVisitor.h" @@ -24,6 +25,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" +#include #include #include #include @@ -225,6 +227,10 @@ static auto hasPointerType() { static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); } +AST_MATCHER(QualType, isCountAttributedType) { + return Node->isCountAttributedType(); +} + AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher, innerMatcher) { const DynTypedMatcher &DTM = static_cast(innerMatcher); @@ -358,14 +364,490 @@ isInUnspecifiedUntypedContext(internal::Matcher InnerMatcher) { return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); } +namespace { + +// Finds the argument that is passed as dependent count. +const Expr *findCountArg(const Expr *Count, const CallExpr *Call) { + const auto *DRE = dyn_cast(Count->IgnoreParenImpCasts()); + if (!DRE) + return nullptr; + + const auto *PVD = dyn_cast(DRE->getDecl()); + if (!PVD) + return nullptr; + + unsigned Index = PVD->getFunctionScopeIndex(); + if (Index >= Call->getNumArgs()) + return nullptr; + + return Call->getArg(Index); +} + +// Mapping: dependent decl -> value. +using DependentValuesTy = llvm::DenseMap; + +// Given the call expr, find the mapping from the dependent parameter to the +// argument that is passed to that parameter. +std::optional +getDependentValuesFromCall(const CountAttributedType *CAT, + const CallExpr *Call) { + DependentValuesTy Values; + for (const auto &DI : CAT->dependent_decls()) { + const auto *PVD = dyn_cast(DI.getDecl()); + if (!PVD) + return std::nullopt; + + unsigned Index = PVD->getFunctionScopeIndex(); + if (Index >= Call->getNumArgs()) + return std::nullopt; + + const Expr *Arg = Call->getArg(Index); + [[maybe_unused]] bool Inserted = Values.insert({PVD, Arg}).second; + assert(Inserted); + } + return {std::move(Values)}; +} + +// Checks if Self and Other are the same member bases. This supports only very +// simple forms of member bases. +bool isSameMemberBase(const Expr *Self, const Expr *Other) { + for (;;) { + if (Self == Other) + return true; + + const auto *SelfICE = dyn_cast(Self); + const auto *OtherICE = dyn_cast(Other); + if (SelfICE && OtherICE && + SelfICE->getCastKind() == OtherICE->getCastKind() && + (SelfICE->getCastKind() == CK_LValueToRValue || + SelfICE->getCastKind() == CK_UncheckedDerivedToBase)) { + Self = SelfICE->getSubExpr(); + Other = OtherICE->getSubExpr(); + } + + const auto *SelfDRE = dyn_cast(Self); + const auto *OtherDRE = dyn_cast(Other); + if (SelfDRE && OtherDRE) + return SelfDRE->getDecl() == OtherDRE->getDecl(); + + if (isa(Self) && isa(Other)) { + // `Self` and `Other` should be evaluated at the same state so `this` must + // mean the same thing for both: + return true; + } + + const auto *SelfME = dyn_cast(Self); + const auto *OtherME = dyn_cast(Other); + if (!SelfME || !OtherME || + SelfME->getMemberDecl() != OtherME->getMemberDecl()) { + return false; + } + + Self = SelfME->getBase(); + Other = OtherME->getBase(); + } +} + +// Impl of `isCompatibleWithCountExpr`. See `isCompatibleWithCountExpr` for +// document. +struct CompatibleCountExprVisitor + : public ConstStmtVisitor { + using BaseVisitor = + ConstStmtVisitor; + + const Expr *MemberBase; + const DependentValuesTy *DependentValues; + ASTContext &Ctx; + + // If `Deref` has the form `*&e`, return `e`; otherwise return nullptr. + const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref) { + const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts(); + + if (const auto *UO = dyn_cast(DerefOperand)) + if (UO->getOpcode() == UO_AddrOf) + return UO->getSubExpr(); + if (const auto *DRE = dyn_cast(DerefOperand)) { + if (!DependentValues) + return nullptr; + + auto I = DependentValues->find(DRE->getDecl()); + + if (I != DependentValues->end()) + if (const auto *UO = dyn_cast(I->getSecond())) + if (UO->getOpcode() == UO_AddrOf) + return UO->getSubExpr(); + } + return nullptr; + } + + explicit CompatibleCountExprVisitor(const Expr *MemberBase, + const DependentValuesTy *DependentValues, + ASTContext &Ctx) + : MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {} + + bool VisitStmt(const Stmt *S, const Expr *E) { return false; } + + bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE, + const Expr *Other) { + return Visit(SelfICE->getSubExpr(), Other); + } + + bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other) { + return Visit(SelfPE->getSubExpr(), Other); + } + + bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other) { + if (const auto *IntLit = + dyn_cast(Other->IgnoreParenImpCasts())) { + return SelfIL == IntLit || + llvm::APInt::isSameValue(SelfIL->getValue(), IntLit->getValue()); + } + return false; + } + + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Self, + const Expr *Other) { + // If `Self` is a `sizeof` expression, try to evaluate and compare the two + // expressions as constants: + if (Self->getKind() == UnaryExprOrTypeTrait::UETT_SizeOf) { + Expr::EvalResult ER; + + if (Self->EvaluateAsConstantExpr(ER, Ctx)) { + APInt SelfVal = ER.Val.getInt(); + + if (Other->getType()->isIntegerType()) + if (Other->EvaluateAsConstantExpr(ER, Ctx)) + return APInt::isSameValue(SelfVal, ER.Val.getInt()); + } + } + return false; + } + + bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other) { + const ValueDecl *SelfVD = SelfDRE->getDecl(); + + if (DependentValues) { + const auto It = DependentValues->find(SelfVD); + if (It != DependentValues->end()) + return Visit(It->second, Other); + } + + const auto *O = Other->IgnoreParenImpCasts(); + + if (const auto *OtherDRE = dyn_cast(O)) { + // Both SelfDRE and OtherDRE can be transformed from member expressions: + if (OtherDRE->getDecl() == SelfVD) + return true; + return false; + } + + const auto *OtherME = dyn_cast(O); + if (MemberBase && OtherME) { + return OtherME->getMemberDecl() == SelfVD && + isSameMemberBase(OtherME->getBase(), MemberBase); + } + + return false; + } + + bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other) { + // Even though we don't support member expression in counted-by, actual + // arguments can be member expressions. + if (Self == Other) + return true; + if (const auto *DRE = dyn_cast(Other->IgnoreParenImpCasts())) + return MemberBase && Self->getMemberDecl() == DRE->getDecl() && + isSameMemberBase(Self->getBase(), MemberBase); + return false; + } + + bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other) { + if (SelfUO->getOpcode() != UO_Deref) + return false; // We don't support any other unary operator + + if (const auto *OtherUO = + dyn_cast(Other->IgnoreParenImpCasts())) { + if (SelfUO->getOpcode() == OtherUO->getOpcode()) + return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr()); + } + // If `Other` is not a dereference expression, try to simplify `SelfUO`: + if (const auto *SimplifiedSelf = trySimplifyDerefAddressof(SelfUO)) { + return Visit(SimplifiedSelf, Other); + } + return false; + } + + bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other) { + const auto *OtherBO = + dyn_cast(Other->IgnoreParenImpCasts()); + if (OtherBO && OtherBO->getOpcode() == SelfBO->getOpcode()) { + return Visit(SelfBO->getLHS(), OtherBO->getLHS()) && + Visit(SelfBO->getRHS(), OtherBO->getRHS()); + } + + return false; + } +}; + +// TL'DR: +// Checks if `E` is the same as `ExpectedCountExpr` modulo implicit casts and +// parens. +// +// Lengthy description: +// `E`: represents the actual count/size associated to a +// pointer. +// `ExpectedCountExpr`: represents the counted-by expression of a counted-by +// type attribute. +// `MemberBase`: represents "this" member base. A MemberExpr +// representing `this->f` in both `E` and +// `ExpectedCountExpr` may be transformed to a DRE +// representing just `f`. Therefore we need to keep track +// of the base for them in that case. +// `DependentValues`: is a mapping from parameter DREs to actual argument +// expressions. It serves as a "state" where +// `ExpectedCountExpr` is "interpreted". +// +// This function then checks for a pointer with a known count `E` that `E` is +// equivalent to the count `ExpectedCountExpr` of a counted-by type attribute at +// the state `DependentValues`. +// +// For example, suppose there is a call to a function `foo(int *__counted_by(n) +// p, size_t n)`: +// +// foo(x, y+z); +// +// We check that the count associated to the pointer 'x' is same as the +// expected count expression 'n' with the mapping (state) '{n -> y+z}'. The +// count of 'x' is determined by pre-defined knowledge, e.g., if 'x' has the +// form of 'span.data()', its' count is 'span.size()', etc. At this point, we +// already know that `E` is the count of 'x'. So we just need to compare `E` +// to 'n' with 'n' being interpreted under '{n -> y+z}'. That is, this function +// will return true iff `E` is same as 'y+z'. +bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr, + const Expr *MemberBase, + const DependentValuesTy *DependentValues, + ASTContext &Ctx) { + CompatibleCountExprVisitor Visitor(MemberBase, DependentValues, Ctx); + return Visitor.Visit(ExpectedCountExpr, E); +} + +// Returns if a pair of expressions contain method calls to .data()/.c_str() and +// .size()/.size_bytes()/.length() that form a valid range. +bool isValidContainerRange(ASTContext &Context, const Expr *Data, + const Expr *Size, bool ArgInBytes, + bool ParamInBytes) { + auto MethodMatcher = [](StringRef ClassName, StringRef MethodName) { + return callee( + cxxMethodDecl(hasName(MethodName), ofClass(hasName(ClassName)))); + }; + + const auto *DataCall = selectFirst( + "e", + match(expr(ignoringParenImpCasts( + cxxMemberCallExpr( + anyOf(MethodMatcher("::std::array", "data"), + MethodMatcher("::std::basic_string", "c_str"), + MethodMatcher("::std::basic_string", "data"), + MethodMatcher("::std::basic_string_view", "data"), + MethodMatcher("::std::span", "data"), + MethodMatcher("::std::vector", "data"))) + .bind("e"))), + *Data, Context)); + if (!DataCall) + return false; + + const auto *SizeCall = selectFirst( + "e", + match(expr(ignoringParenImpCasts( + cxxMemberCallExpr( + anyOf(MethodMatcher("::std::array", "size"), + MethodMatcher("::std::basic_string", "length"), + MethodMatcher("::std::basic_string", "size"), + MethodMatcher("::std::basic_string_view", "length"), + MethodMatcher("::std::basic_string_view", "size"), + MethodMatcher("::std::span", "size"), + MethodMatcher("::std::span", "size_bytes"), + MethodMatcher("::std::vector", "size"))) + .bind("e"))), + *Size, Context)); + if (!SizeCall) + return false; + + const Expr *DataObj = DataCall->getImplicitObjectArgument(); + const Expr *SizeObj = SizeCall->getImplicitObjectArgument(); + if (!DataObj || !SizeObj) + return false; + + bool AllowSizeBytes = ParamInBytes; + bool RequireSizeBytes = !ArgInBytes && ParamInBytes; + bool IsSizeBytes = SizeCall->getDirectCallee()->getName() == "size_bytes"; + if (!AllowSizeBytes && IsSizeBytes) + return false; + if (RequireSizeBytes && !IsSizeBytes) + return false; + + // Check if the base of data and size is the same. + const auto *DataDRE = dyn_cast(DataObj->IgnoreImpCasts()); + const auto *SizeDRE = dyn_cast(SizeObj->IgnoreImpCasts()); + return DataDRE && SizeDRE && DataDRE->getDecl() == SizeDRE->getDecl(); +} + +// Extract the extent `X` from `sp.first(X).data()` and friends. +const Expr *extractExtentFromSubviewDataCall(ASTContext &Context, + const Expr *E) { + auto ExtentMatcher = [](StringRef Name, unsigned N) { + return cxxMemberCallExpr( + callee(cxxMethodDecl(hasName(Name), ofClass(hasName("::std::span")))), + hasArgument(N, expr().bind("extent"))); + }; + auto SpanSubviewMatcher = + anyOf(ExtentMatcher("first", 0), ExtentMatcher("last", 0), + ExtentMatcher("subspan", 1)); + auto SpanDataMatcher = cxxMemberCallExpr( + callee(cxxMethodDecl(hasName("data"), ofClass(hasName("::std::span")))), + on(SpanSubviewMatcher)); + return selectFirst( + "extent", + match(expr(ignoringParenImpCasts(SpanDataMatcher)), *E, Context)); +} + +// Returns true iff `E` evaluates to `Val`. +static bool hasIntegeralConstant(const Expr *E, uint64_t Val, ASTContext &Ctx) { + Expr::EvalResult ER; + + if (E->EvaluateAsConstantExpr(ER, Ctx)) { + APInt Eval = ER.Val.getInt(); + + return APInt::isSameValue(Eval, APInt(Eval.getBitWidth(), Val)); + } + return false; +} + +// Checks if the argument passed to count-attributed pointer is one of the +// following forms: +// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`. +// 1. `&var`, if `var` is a variable identifier and the dependent count is `1`. +// 2. `&var`, if `var` is a variable identifier and the dependent size is +// equivalent to `sizeof(var)`. +// 3. `sp.data()` if the argument to dependent count is `sp.size()`. +// 4. `sp.first(extent).data()` if `extent` is compatible with the `count`. +// 5. `p` if `p` is __counted_by(c) pointer and `c` is compatible with the +// `count` of the param type. +// 6. `constant-array` if the argument to dependent count is the length of +// `constant-array`. +// 7. `(T*) constant-array` if the size of `T` equals to one byte and the +// argument to dependent size is equivalent to `sizeof(constant-array)`. +bool isCountAttributedPointerArgumentSafe(ASTContext &Context, + const CountAttributedType *CAT, + const CallExpr *Call, + const Expr *Arg) { + const Expr *ArgNoImp = Arg->IgnoreParenImpCasts(); + + // check form 0: + if (ArgNoImp->getType()->isNullPtrType()) { + if (const auto *CountArg = findCountArg(CAT->getCountExpr(), Call)) + return hasIntegeralConstant(CountArg, 0, Context); + return false; + } + + // check form 1-2: + auto AddressofDRE = expr(unaryOperator( + hasOperatorName("&"), + hasUnaryOperand(ignoringParenImpCasts(declRefExpr().bind("VarIdent"))))); + + if (auto *DRE = selectFirst( + "VarIdent", match(AddressofDRE, *ArgNoImp, Context))) { + if (const auto *CountArg = findCountArg(CAT->getCountExpr(), Call)) { + if (!CAT->isCountInBytes()) // form 1: + return hasIntegeralConstant(CountArg, 1, Context); + // form 2: + if (auto TySize = Context.getTypeSizeInCharsIfKnown(DRE->getType())) + return hasIntegeralConstant(CountArg, TySize->getQuantity(), Context); + } + return false; + } + + auto getTypeSize = [&](QualType Ty) -> std::optional { + if (Context.hasSameUnqualifiedType(Ty, Context.VoidTy)) + return {CharUnits::One()}; + return Context.getTypeSizeInCharsIfKnown(Ty); + }; + + std::optional ArgTypeSize = getTypeSize( + QualType(ArgNoImp->getType()->getPointeeOrArrayElementType(), 0)); + std::optional ParamTypeSize = getTypeSize(CAT->getPointeeType()); + + bool ArgInBytes = ArgTypeSize.has_value() && ArgTypeSize->isOne(); + bool ParamInBytes = CAT->isCountInBytes() || + (ParamTypeSize.has_value() && ParamTypeSize->isOne()); + + // If there is only one dependent count, check for the form 3. + if (const auto *CountArg = findCountArg(CAT->getCountExpr(), Call)) { + if (isValidContainerRange(Context, Arg, CountArg, ArgInBytes, ParamInBytes)) + return true; + } + + // Check forms 4-7. + + if (ArgInBytes != ParamInBytes) + return false; + + auto ValuesOpt = getDependentValuesFromCall(CAT, Call); + if (!ValuesOpt.has_value()) + return false; + + const Expr *ArgCount = nullptr; + const Expr *MemberBase = nullptr; + + if (const auto *ME = dyn_cast(ArgNoImp)) + MemberBase = ME->getBase(); + + if (const Expr *ExtentExpr = extractExtentFromSubviewDataCall(Context, Arg)) { + // Form 4. + ArgCount = ExtentExpr; + } else if (const auto *ArgCAT = + ArgNoImp->getType()->getAs()) { + // Form 5. + if (ArgCAT->isOrNull() == CAT->isOrNull()) + ArgCount = ArgCAT->getCountExpr(); + } else { + // Form 6-7. + const Expr *ArrArg = ArgNoImp; + + if (ArgInBytes) + if (auto *CE = dyn_cast(ArrArg)) + // In case of ArgInBytes, we know the destination type is a pointer to + // char: + ArrArg = CE->getSubExpr()->IgnoreParenImpCasts(); + if (const auto *ATy = Context.getAsConstantArrayType(ArrArg->getType())) { + APInt TySize = ATy->getSize(); + + if (CAT->isCountInBytes()) + if (auto TySizeInChar = Context.getTypeSizeInCharsIfKnown(ATy)) + TySize = APInt(TySize.getBitWidth(), TySizeInChar->getQuantity()); + ArgCount = IntegerLiteral::Create(Context, TySize, Context.getSizeType(), + SourceLocation()); + } + } + if (!ArgCount) + return false; + + return isCompatibleWithCountExpr(ArgCount, CAT->getCountExpr(), MemberBase, + &*ValuesOpt, Context); +} + +} // namespace + // Given a two-param std::span construct call, matches iff the call has the // following forms: // 1. `std::span{new T[n], n}`, where `n` is a literal or a DRE // 2. `std::span{new T, 1}` -// 3. `std::span{&var, 1}` +// 3. `std::span{&var, 1}` or `std::span{std::addressof(...), 1}` // 4. `std::span{a, n}`, where `a` is of an array-of-T with constant size // `n` // 5. `std::span{any, 0}` +// 6. `std::span{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`) +// pointer. AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { assert(Node.getNumArgs() == 2 && "expecting a two-parameter std::span constructor"); @@ -410,6 +892,15 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { // Check form 3: return Arg1CV && Arg1CV->isOne(); break; + case Stmt::CallExprClass: + if (const auto *CE = dyn_cast(Arg0)) { + const auto FnDecl = CE->getDirectCallee(); + if (FnDecl && FnDecl->getNameAsString() == "addressof" && + FnDecl->isInStdNamespace()) { + return Arg1CV && Arg1CV->isOne(); + } + } + break; default: break; } @@ -423,6 +914,35 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { // Check form 4: return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0; } + + // Check form 6: + if (const auto *CAT = Arg0Ty->getAs()) { + // Accept __sized_by() if the size of the pointee type is 1. + if (CAT->isCountInBytes()) { + std::optional SizeOpt = + Finder->getASTContext().getTypeSizeInCharsIfKnown( + CAT->getPointeeType()); + if (!SizeOpt.has_value() || !SizeOpt->isOne()) + return false; + } + + const Expr *MemberBase = nullptr; + if (const auto *ME = dyn_cast(Arg0)) + MemberBase = ME->getBase(); + + if (const auto *Call = dyn_cast(Arg0)) { + auto ValuesOpt = getDependentValuesFromCall(CAT, Call); + if (!ValuesOpt.has_value()) + return false; + return isCompatibleWithCountExpr(Arg1, CAT->getCountExpr(), MemberBase, + &*ValuesOpt, Finder->getASTContext()); + } + + return isCompatibleWithCountExpr(Arg1, CAT->getCountExpr(), MemberBase, + /*DependentValues=*/nullptr, + Finder->getASTContext()); + } + return false; } @@ -434,39 +954,71 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) { // already duplicated // - call both from Sema and from here - const auto *BaseDRE = - dyn_cast(Node.getBase()->IgnoreParenImpCasts()); - const auto *SLiteral = - dyn_cast(Node.getBase()->IgnoreParenImpCasts()); - uint64_t size; - - if (!BaseDRE && !SLiteral) + uint64_t limit; + if (const auto *CATy = + dyn_cast(Node.getBase() + ->IgnoreParenImpCasts() + ->getType() + ->getUnqualifiedDesugaredType())) { + limit = CATy->getLimitedSize(); + } else if (const auto *SLiteral = dyn_cast( + Node.getBase()->IgnoreParenImpCasts())) { + limit = SLiteral->getLength() + 1; + } else { return false; - - if (BaseDRE) { - if (!BaseDRE->getDecl()) - return false; - const auto *CATy = Finder->getASTContext().getAsConstantArrayType( - BaseDRE->getDecl()->getType()); - if (!CATy) { - return false; - } - size = CATy->getLimitedSize(); - } else if (SLiteral) { - size = SLiteral->getLength() + 1; } - if (const auto *IdxLit = dyn_cast(Node.getIdx())) { - const APInt ArrIdx = IdxLit->getValue(); + Expr::EvalResult EVResult; + const Expr *IndexExpr = Node.getIdx(); + if (!IndexExpr->isValueDependent() && + IndexExpr->EvaluateAsInt(EVResult, Finder->getASTContext())) { + llvm::APSInt ArrIdx = EVResult.Val.getInt(); // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a // bug - if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < size) + if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit) return true; } - return false; } +// Matches each argument passed to a `__counted_by(count)` pointer that is +// unsafe, i.e. that is NOT in one of the following forms: +// 1. `sp.data()` if the argument to dependent count is `sp.size()`. +// 2. `sp.first(extent).data()` if `extent` is compatible with the `count`. +AST_MATCHER_P(CallExpr, forEachUnsafeCountAttributedPointerArgument, + internal::Matcher, ArgMatcher) { + ASTContext &Context = Finder->getASTContext(); + const CallExpr *Call = &Node; + + const FunctionDecl *FD = Call->getDirectCallee(); + if (!FD) + return false; + + const auto *FPT = FD->getType()->getAs(); + if (!FPT) + return false; + + const unsigned Num = std::min(Call->getNumArgs(), FPT->getNumParams()); + + bool Matched = false; + for (unsigned I = 0; I < Num; ++I) { + const auto *CAT = FPT->getParamType(I)->getAs(); + if (!CAT) + continue; + + const Expr *Arg = Call->getArg(I); + if (!isCountAttributedPointerArgumentSafe(Context, CAT, Call, Arg)) { + BoundNodesTreeBuilder ArgMatches(*Builder); + if (ArgMatcher.matches(*Arg, Finder, &ArgMatches)) { + Builder->addMatch(ArgMatches); + Matched = true; + } + } + } + + return Matched; +} + AST_MATCHER_P(CallExpr, hasNumArgs, unsigned, Num) { return Node.getNumArgs() == Num; } @@ -1987,6 +2539,53 @@ class DerefSimplePtrArithFixableGadget : public FixableGadget { } }; +// Represents an argument that is being passed to a __counted_by() pointer. +class CountAttributedPointerArgumentGadget : public WarningGadget { +private: + static constexpr const char *const CallTag = + "CountAttributedPointerArgument_Call"; + static constexpr const char *const ArgTag = + "CountAttributedPointerArgument_Arg"; + const CallExpr *Call; + const Expr *Arg; + +public: + explicit CountAttributedPointerArgumentGadget( + const MatchFinder::MatchResult &Result) + : WarningGadget(Kind::CountAttributedPointerArgument), + Call(Result.Nodes.getNodeAs(CallTag)), + Arg(Result.Nodes.getNodeAs(ArgTag)) { + assert(Call != nullptr && "Expecting a non-null matching result"); + assert(Arg != nullptr && "Expecting a non-null matching result"); + } + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::CountAttributedPointerArgument; + } + + static Matcher matcher() { + return stmt(callExpr(forEachUnsafeCountAttributedPointerArgument( + expr().bind(ArgTag))) + .bind(CallTag)); + } + + void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, + bool IsRelatedToDecl, + ASTContext &Ctx) const override { + Handler.handleUnsafeCountAttributedPointerArgument(Call, Arg, + IsRelatedToDecl, Ctx); + } + + SourceLocation getSourceLoc() const override { return Arg->getBeginLoc(); } + + virtual DeclUseList getClaimedVarUseSites() const override { + if (const auto *DRE = dyn_cast(Arg)) { + return {DRE}; + } + return {}; + } +}; + /// Scan the function and return a list of gadgets found with provided kits. static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 4f7ccaf4021d6..4575f6e8ea0e3 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -109,10 +109,14 @@ namespace { KEYCUDA = 0x1000000, KEYHLSL = 0x2000000, KEYFIXEDPOINT = 0x4000000, - KEYMAX = KEYFIXEDPOINT, // The maximum key + KEYBOUNDSSAFETY = 0x8000000, + KEYBOUNDSSAFETYATTRIBUTES = 0x10000000, + KEYMAX = KEYBOUNDSSAFETYATTRIBUTES, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 & ~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude. + // Exclude bounds-safety. + & ~KEYBOUNDSSAFETY & ~KEYBOUNDSSAFETYATTRIBUTES }; /// How a keyword is treated in the selected standard. This enum is ordered @@ -213,6 +217,12 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts, return KS_Unknown; case KEYFIXEDPOINT: return LangOpts.FixedPoint ? KS_Enabled : KS_Disabled; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case KEYBOUNDSSAFETY: + return LangOpts.BoundsSafety ? KS_Enabled : KS_Unknown; + case KEYBOUNDSSAFETYATTRIBUTES: + return LangOpts.BoundsSafetyAttributes ? KS_Enabled : KS_Unknown; + /* TO_UPSTREAM(BoundsSafety) OFF*/ default: llvm_unreachable("Unknown KeywordStatus flag"); } diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 71ce08a00178e..121a92f67a4de 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -312,6 +312,13 @@ bool Module::directlyUses(const Module *Requested) { if (!Requested->Parent && Requested->Name == "ptrauth") return true; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Darwin is allowed is to use our builtin 'ptrcheck.h' and its accompanying + // module. + if (!Requested->Parent && Requested->Name == "ptrcheck") + return true; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (NoUndeclaredIncludes) UndeclaredUses.insert(Requested); diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 29133f9ee8fce..96755a63e0257 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -135,11 +135,15 @@ std::unique_ptr AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::aarch64_32: if (Triple.isOSDarwin()) return std::make_unique(Triple, Opts); + else if (Triple.isAppleMachO()) + return std::make_unique(Triple, Opts); return nullptr; case llvm::Triple::aarch64: if (Triple.isOSDarwin()) return std::make_unique(Triple, Opts); + else if (Triple.isAppleMachO()) + return std::make_unique(Triple, Opts); switch (os) { case llvm::Triple::FreeBSD: @@ -243,6 +247,8 @@ std::unique_ptr AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::thumbeb: if (Triple.isOSDarwin()) return std::make_unique(Triple, Opts); + else if (Triple.isAppleMachO()) + return std::make_unique(Triple, Opts); switch (os) { case llvm::Triple::Linux: @@ -534,6 +540,8 @@ std::unique_ptr AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::x86: if (Triple.isOSDarwin()) return std::make_unique(Triple, Opts); + else if (Triple.isAppleMachO()) + return std::make_unique(Triple, Opts); switch (os) { case llvm::Triple::Linux: { diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index b2b2f5695c0e4..495d43e542ff5 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -1652,6 +1652,10 @@ MinGWARM64TargetInfo::MinGWARM64TargetInfo(const llvm::Triple &Triple, TheCXXABI.set(TargetCXXABI::GenericAArch64); } +AppleMachOAArch64TargetInfo::AppleMachOAArch64TargetInfo( + const llvm::Triple &Triple, const TargetOptions &Opts) + : AppleMachOTargetInfo(Triple, Opts) {} + DarwinAArch64TargetInfo::DarwinAArch64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) : DarwinTargetInfo(Triple, Opts) { @@ -1676,9 +1680,9 @@ DarwinAArch64TargetInfo::DarwinAArch64TargetInfo(const llvm::Triple &Triple, TheCXXABI.set(TargetCXXABI::AppleARM64); } -void DarwinAArch64TargetInfo::getOSDefines(const LangOptions &Opts, - const llvm::Triple &Triple, - MacroBuilder &Builder) const { +void clang::targets::getAppleMachOAArch64Defines(MacroBuilder &Builder, + const LangOptions &Opts, + const llvm::Triple &Triple) { Builder.defineMacro("__AARCH64_SIMD__"); if (Triple.isArch32Bit()) Builder.defineMacro("__ARM64_ARCH_8_32__"); @@ -1691,7 +1695,20 @@ void DarwinAArch64TargetInfo::getOSDefines(const LangOptions &Opts, if (Triple.isArm64e()) Builder.defineMacro("__arm64e__", "1"); +} +void AppleMachOAArch64TargetInfo::getOSDefines(const LangOptions &Opts, + const llvm::Triple &Triple, + MacroBuilder &Builder) const { + getAppleMachOAArch64Defines(Builder, Opts, Triple); + AppleMachOTargetInfo::getOSDefines(Opts, Triple, + Builder); +} + +void DarwinAArch64TargetInfo::getOSDefines(const LangOptions &Opts, + const llvm::Triple &Triple, + MacroBuilder &Builder) const { + getAppleMachOAArch64Defines(Builder, Opts, Triple); DarwinTargetInfo::getOSDefines(Opts, Triple, Builder); } diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 526f7f30a3861..9786cfe889e06 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -265,6 +265,20 @@ class LLVM_LIBRARY_VISIBILITY AArch64beTargetInfo : public AArch64TargetInfo { void setDataLayout() override; }; +void getAppleMachOAArch64Defines(MacroBuilder &Builder, const LangOptions &Opts, + const llvm::Triple &Triple); + +class LLVM_LIBRARY_VISIBILITY AppleMachOAArch64TargetInfo + : public AppleMachOTargetInfo { +public: + AppleMachOAArch64TargetInfo(const llvm::Triple &Triple, + const TargetOptions &Opts); + +protected: + void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const override; +}; + class LLVM_LIBRARY_VISIBILITY DarwinAArch64TargetInfo : public DarwinTargetInfo { public: diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index e55feedbd5c6f..329f690d6d9d5 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -1475,6 +1475,16 @@ void CygwinARMTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("_GNU_SOURCE"); } +AppleMachOARMTargetInfo::AppleMachOARMTargetInfo(const llvm::Triple &Triple, + const TargetOptions &Opts) + : AppleMachOTargetInfo(Triple, Opts) {} + +void AppleMachOARMTargetInfo::getOSDefines(const LangOptions &Opts, + const llvm::Triple &Triple, + MacroBuilder &Builder) const { + getAppleMachODefines(Builder, Opts, Triple); +} + DarwinARMTargetInfo::DarwinARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) : DarwinTargetInfo(Triple, Opts) { diff --git a/clang/lib/Basic/Targets/ARM.h b/clang/lib/Basic/Targets/ARM.h index df9855a52e61c..f64e3782f165e 100644 --- a/clang/lib/Basic/Targets/ARM.h +++ b/clang/lib/Basic/Targets/ARM.h @@ -300,6 +300,17 @@ class LLVM_LIBRARY_VISIBILITY CygwinARMTargetInfo : public ARMleTargetInfo { MacroBuilder &Builder) const override; }; +class LLVM_LIBRARY_VISIBILITY AppleMachOARMTargetInfo + : public AppleMachOTargetInfo { +protected: + void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const override; + +public: + AppleMachOARMTargetInfo(const llvm::Triple &Triple, + const TargetOptions &Opts); +}; + class LLVM_LIBRARY_VISIBILITY DarwinARMTargetInfo : public DarwinTargetInfo { protected: diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index 3081127a17bf9..41d7112b9e1cb 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -20,15 +20,13 @@ using namespace clang::targets; namespace clang { namespace targets { -void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, - const llvm::Triple &Triple, StringRef &PlatformName, - VersionTuple &PlatformMinVersion) { +void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts, + const llvm::Triple &Triple) { Builder.defineMacro("__APPLE_CC__", "6000"); Builder.defineMacro("__APPLE__"); - Builder.defineMacro("__STDC_NO_THREADS__"); // AddressSanitizer doesn't play well with source fortification, which is on - // by default on Darwin. + // by default on Apple platforms. if (Opts.Sanitize.has(SanitizerKind::Address)) Builder.defineMacro("_FORTIFY_SOURCE", "0"); @@ -39,7 +37,7 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Builder.defineMacro("__ptrauth_abi_version__", llvm::utostr(Opts.PointerAuthABIVersion)); - // Darwin defines __weak, __strong, and __unsafe_unretained even in C mode. + // Apple defines __weak, __strong, and __unsafe_unretained even in C mode. if (!Opts.ObjC) { // __weak is always defined, for use in blocks and with objc pointers. Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); @@ -55,6 +53,22 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, if (Opts.POSIXThreads) Builder.defineMacro("_REENTRANT"); + // __MACH__ originally meant "will run in a Mach kernel based OS", but it has + // come to also mean "uses Apple Mach-O linking/symbol visibility semantics". + // Notably libc++'s __configuration/platform.h and Swift's shims/Visibility.h + // take __MACH__ for the more general meaning. + if (Triple.isAppleMachO() || Triple.isOSDarwin()) + Builder.defineMacro("__MACH__"); +} + +void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, + const llvm::Triple &Triple, StringRef &PlatformName, + VersionTuple &PlatformMinVersion) { + getAppleMachODefines(Builder, Opts, Triple); + + // Darwin's libc doesn't have threads.h + Builder.defineMacro("__STDC_NO_THREADS__"); + // Get the platform type and version number from the triple. VersionTuple OsVersion; if (Triple.isMacOSX()) { @@ -122,9 +136,6 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, assert(OsVersion.getMinor().value_or(0) < 100 && OsVersion.getSubminor().value_or(0) < 100 && "Invalid version!"); Builder.defineMacro("__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__", Str); - - // Tell users about the kernel if there is one. - Builder.defineMacro("__MACH__"); } PlatformMinVersion = OsVersion; diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h index 014ac3f22cb81..ec4fa159ec819 100644 --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/clang/lib/Basic/Targets/OSTargets.h @@ -34,12 +34,39 @@ class LLVM_LIBRARY_VISIBILITY OSTargetInfo : public TgtInfo { } }; +void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts, + const llvm::Triple &Triple); + void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, const llvm::Triple &Triple, StringRef &PlatformName, VersionTuple &PlatformMinVersion); template -class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo : public OSTargetInfo { +class LLVM_LIBRARY_VISIBILITY AppleMachOTargetInfo + : public OSTargetInfo { +protected: + void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const override { + getAppleMachODefines(Builder, Opts, Triple); + } + +public: + AppleMachOTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) + : OSTargetInfo(Triple, Opts) {} + + const char *getStaticInitSectionSpecifier() const override { + return "__TEXT,__StaticInit,regular,pure_instructions"; + } + + /// Apple Mach-O does not support protected visibility. Its "default" is very + /// similar to ELF's "protected"; Apple Mach-O requires a "weak" attribute on + /// declarations that can be dynamically replaced. + bool hasProtectedVisibility() const override { return false; } +}; + +template +class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo + : public AppleMachOTargetInfo { protected: void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const override { @@ -49,7 +76,7 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo : public OSTargetInfo { public: DarwinTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) - : OSTargetInfo(Triple, Opts) { + : AppleMachOTargetInfo(Triple, Opts) { // By default, no TLS, and we list permitted architecture/OS // combinations. this->TLSSupported = false; @@ -82,14 +109,9 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo : public OSTargetInfo { const char *getStaticInitSectionSpecifier() const override { // FIXME: We should return 0 when building kexts. - return "__TEXT,__StaticInit,regular,pure_instructions"; + return AppleMachOTargetInfo::getStaticInitSectionSpecifier(); } - /// Darwin does not support protected visibility. Darwin's "default" - /// is very similar to ELF's "protected"; Darwin requires a "weak" - /// attribute on declarations that can be dynamically replaced. - bool hasProtectedVisibility() const override { return false; } - unsigned getExnObjectAlignment() const override { // Older versions of libc++abi guarantee an alignment of only 8-bytes for // exception objects because of a bug in __cxa_exception that was diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 58de7db12dd32..5f49ff60e2b27 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -525,6 +525,14 @@ class LLVM_LIBRARY_VISIBILITY OpenBSDI386TargetInfo } }; +class LLVM_LIBRARY_VISIBILITY AppleMachOI386TargetInfo + : public AppleMachOTargetInfo { +public: + AppleMachOI386TargetInfo(const llvm::Triple &Triple, + const TargetOptions &Opts) + : AppleMachOTargetInfo(Triple, Opts) {} +}; + class LLVM_LIBRARY_VISIBILITY DarwinI386TargetInfo : public DarwinTargetInfo { public: diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp b/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp new file mode 100644 index 0000000000000..63aa2506c807d --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp @@ -0,0 +1,47 @@ +//===- BoundsSafetyOptRemarks.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace CodeGen { + +llvm::StringRef GetBoundsSafetyOptRemarkString(BoundsSafetyOptRemarkKind kind) { + switch (kind) { + case BNS_OR_NONE: + return ""; +#define BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) \ + case BNS_OR_##SUFFIX: \ + return ANNOTATION_STR; +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_OR + } + llvm_unreachable("Unhandled BoundsSafetyOptRemarkKind"); +} + +BoundsSafetyOptRemarkKind GetBoundsSafetyOptRemarkForTrap(BoundsSafetyTrapKind kind) { + switch (kind) { + case BNS_TRAP_NONE: + return BNS_OR_NONE; +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) \ + case BNS_TRAP_##SUFFIX: \ + return BNS_OR_##SUFFIX; +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) \ + case BNS_TRAP_##SUFFIX: \ + return BNS_OR_##SUFFIX; +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX + } + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.def b/clang/lib/CodeGen/BoundsSafetyOptRemarks.def new file mode 100644 index 0000000000000..65dcfcbe0422e --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.def @@ -0,0 +1,26 @@ +//===- BoundsSafetyOptRemarks.def ----------------------------------0----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file declares the different types of BoundsSafety opt-remarks that can +// emitted. The opt-remarks added here should be independent of any BoundsSafety +// trap. If the opt-remark is tied to a particular trap you should declare it +// in `BoundsSafetyTraps.def` instead. +//===----------------------------------------------------------------------===// +#ifndef BOUNDS_SAFETY_OR +#error BOUNDS_SAFETY_OR must be defined +#endif + +// BOUNDS_SAFETY_OR(, ) + +BOUNDS_SAFETY_OR(PTR_NEQ_NULL, "bounds-safety-check-ptr-neq-null") + +// Include opt-remarks from BoundsSafety traps +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, _) BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.h b/clang/lib/CodeGen/BoundsSafetyOptRemarks.h new file mode 100644 index 0000000000000..077a34ee46bf0 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.h @@ -0,0 +1,30 @@ +//===- BoundsSafetyOptRemarks.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_OPT_REMARKS_H +#define LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_OPT_REMARKS_H +#include "BoundsSafetyTraps.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instruction.h" + +namespace clang { +namespace CodeGen { + +enum BoundsSafetyOptRemarkKind { + BNS_OR_NONE, ///< Special value representing no opt-remark. +#define BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) BNS_OR_##SUFFIX, +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_OR +}; + +llvm::StringRef GetBoundsSafetyOptRemarkString(BoundsSafetyOptRemarkKind kind); +BoundsSafetyOptRemarkKind GetBoundsSafetyOptRemarkForTrap(BoundsSafetyTrapKind kind); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.cpp b/clang/lib/CodeGen/BoundsSafetyTraps.cpp new file mode 100644 index 0000000000000..5ef672ea9da99 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.cpp @@ -0,0 +1,82 @@ +//===- BoundsSafetyTraps.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "BoundsSafetyTraps.h" +#include "BoundsSafetyOptRemarks.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace CodeGen { + +llvm::StringRef GetBoundsSafetyTrapMessagePrefix() { + return "Bounds check failed"; +} + +llvm::StringRef +GetBoundsSafetyTrapMessageSuffixWithContext(BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + switch (kind) { + case BNS_TRAP_PTR_LT_LOWER_BOUND: + switch (TrapCtx) { + case BoundsSafetyTrapCtx::DEREF: + return "Dereferencing below bounds"; + case BoundsSafetyTrapCtx::CAST: + return "Pointer below bounds while casting"; + case BoundsSafetyTrapCtx::ASSIGN: + return "Pointer below bounds while assigning"; + case BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER: + return "Pointer to struct below bounds while taking address of struct " + "member"; + case BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE: + default: + llvm_unreachable("Invalid TrapCtx"); + } + case BNS_TRAP_PTR_GT_UPPER_BOUND: + case BNS_TRAP_PTR_GE_UPPER_BOUND: + switch (TrapCtx) { + case BoundsSafetyTrapCtx::DEREF: + return "Dereferencing above bounds"; + case BoundsSafetyTrapCtx::CAST: + return "Pointer above bounds while casting"; + case BoundsSafetyTrapCtx::ASSIGN: + return "Pointer above bounds while assigning"; + case BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER: + return "Pointer to struct above bounds while taking address of struct " + "member"; + case BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE: + return "Pointer above bounds while converting __indexable to " + "__terminated_by"; + default: + llvm_unreachable("Invalid TrapCtx"); + } + default: + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); + } +} + +llvm::StringRef GetBoundsSafetyTrapMessageSuffix(BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + switch (kind) { + case BNS_TRAP_NONE: + return ""; +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) \ + case BNS_TRAP_##SUFFIX: \ + return GetBoundsSafetyTrapMessageSuffixWithContext(kind, TrapCtx); +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) \ + case BNS_TRAP_##SUFFIX: \ + return TRAP_MSG; +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX + } + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.def b/clang/lib/CodeGen/BoundsSafetyTraps.def new file mode 100644 index 0000000000000..bd122b87d84f8 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.def @@ -0,0 +1,102 @@ +//===- BoundsSafetyTraps.def ----------------------------------0----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file declares the different types of BoundsSafety traps that can emitted +// along with properties of the trap +//===----------------------------------------------------------------------===// + +#ifndef BOUNDS_SAFETY_TRAP +#error BOUNDS_SAFETY_TRAP must be defined +#endif +#ifndef BOUNDS_SAFETY_TRAP_CTX +#error BOUNDS_SAFETY_TRAP_CTX must be defined +#endif + +// BOUNDS_SAFETY_TRAP( +// , +// , +// ) +// +// - The name of the enum field (without the prefix). This +// should be a valid enum identifier. The name of this field should indicate (if +// possible) the condition that is true when the trap fires. +// +// - The string is used to annotate instructions +// (that might lead to the trap) with opt remarks. The naming convention +// used here is that the string should (if possible) describe the condition +// that the annotated branch instruction checks. More specifically it is the +// condition that is true when execution should continue (i.e. not trap). +// NOTE THIS NAMING CONVENTION IS THE OPPOSITE TO WHAT IS USED FOR THE OTHER +// FIELDS. It is setup this way for historical reasons. +// +// - The string used to describe the type of trap to the user. If +// possible the string should describe the condition that was checked for, but +// failed to hold. +// +// ----------------------------------------------------------------------------- +// +// BOUNDS_SAFETY_TRAP_CTX(, ) +// +// Same as `BOUNDS_SAFETY_TRAP` but does not take a `` because the +// message depends on context so it is specified elsewhere. + +// The general trap message is deliberately empty because we have nothing useful +// to tell the user other that it's a BoundsSafety trap which is already handled by +// the prefix (`GetBoundsSafetyTrapMessagePrefix()`) that is emitted for all +// BoundsSafety traps. +BOUNDS_SAFETY_TRAP( + GENERAL, "bounds-safety-generic", "") + +BOUNDS_SAFETY_TRAP_CTX(PTR_GT_UPPER_BOUND, "bounds-safety-check-ptr-le-upper-bound") +BOUNDS_SAFETY_TRAP_CTX(PTR_GE_UPPER_BOUND, "bounds-safety-check-ptr-lt-upper-bound") +BOUNDS_SAFETY_TRAP_CTX(PTR_LT_LOWER_BOUND, "bounds-safety-check-ptr-ge-lower-bound") + +BOUNDS_SAFETY_TRAP( + BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND, + "bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound", + "Converted __indexable pointer is below bounds") +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_PTR_ARITH, "bounds-safety-check-terminated-by-ptr-arith", + "Arithmetic on __terminated_by pointer one-past-the-end of the terminator") +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_TERM_ASSIGN, "bounds-safety-check-terminated-by-term-assign", + "The terminator cannot be assigned") + +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR, + "bounds-safety-check-terminated-by-from-indexable-ptr-le-term-ptr", + "Terminator pointer below bounds" +) +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW, + "bounds-safety-check-terminated-by-from-indexable-term-ptr-no-overflow", + "Terminator pointer overflows address space" +) +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND, + "bounds-safety-check-terminated-by-from-indexable-term-ptr-plus-one-le-upper-bound", + "Terminator pointer above bounds" +) + +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_FROM_INDEXABLE_TERM, + "bounds-safety-check-terminated-by-from-indexable-term", + "Cannot find the terminator when converting to __terminated_by pointer from an __indexable pointer") +BOUNDS_SAFETY_TRAP( + INDEXABLE_PTR_NEW_LT_OLD, + "bounds-safety-check-new-indexable-ptr-ge-old", + "New lower bound less than old lower bound" +) +BOUNDS_SAFETY_TRAP( + COUNT_NEGATIVE, + "bounds-safety-check-count-negative", + "Count is negative") +BOUNDS_SAFETY_TRAP( + FLEX_COUNT_GT_BOUNDS, + "bounds-safety-check-flexible-count-gt-bounds", + "Count for flexible array member is too big") +BOUNDS_SAFETY_TRAP( + PTR_PAST_END_OVERFLOW, + "bounds-safety-check-one-past-end-overflow", + "Pointer to one past the end overflows address space") \ No newline at end of file diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.h b/clang/lib/CodeGen/BoundsSafetyTraps.h new file mode 100644 index 0000000000000..5a96709326826 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.h @@ -0,0 +1,49 @@ +//===- BoundsSafetyTraps.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_TRAPS_H +#define LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_TRAPS_H +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace CodeGen { + +enum BoundsSafetyTrapKind { + BNS_TRAP_NONE, ///< Special value representing no trap. +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) BNS_TRAP_##SUFFIX, +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) BNS_TRAP_##SUFFIX, +#include "BoundsSafetyTraps.def" + +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX +}; + +llvm::StringRef GetBoundsSafetyTrapMessagePrefix(); + +// BoundsSafetyTrapCtx provides an enum and helper methods for describing the +// context where a trap happens (i.e. the operation we are guarding against with +// a trap). +struct BoundsSafetyTrapCtx { + enum Kind { + UNKNOWN, ///< Unknown + DEREF, ///< Pointer/Array dereference + ASSIGN, ///< Assign + CAST, ///< Cast + ADDR_OF_STRUCT_MEMBER, ///< Take address of struct member + TERMINATED_BY_FROM_INDEXABLE ///< Check during call to + ///< `__unsafe_terminated_by_from_indexable`. + }; +}; + +llvm::StringRef GetBoundsSafetyTrapMessageSuffix( + BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index b2274b68d915f..9b6cdf451333a 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2846,7 +2846,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, break; } - if (FI.getExtParameterInfo(ArgNo).isNoEscape()) + if (FI.getExtParameterInfo(ArgNo).isNoEscape() && + isValidPointerAttrType(ParamType, /*RefOkay=*/true)) Attrs.addAttribute(llvm::Attribute::NoCapture); if (Attrs.hasAttributes()) { diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index b3d5a0b408fef..bc774cde2a3a1 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1082,6 +1082,30 @@ llvm::DIType *CGDebugInfo::CreateType(const ObjCObjectPointerType *Ty, llvm::DIType *CGDebugInfo::CreateType(const PointerType *Ty, llvm::DIFile *Unit) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // If we heve a -fbounds-safety pointer then we should grab the wide_ptr it + // maps to from the ASTContext and generate that. + if (!Ty->hasRawPointerLayout()) { + llvm::StructType *StructTy = + cast(convertTypeForMemory(CGM, QualType(Ty, 0))); + SmallVector EltTys; + QualType ElemQTy = CGM.getContext().getPointerType(Ty->getPointeeType()); + std::string MemberNames[] = {"ptr", "ub", "lb"}; + uint64_t FieldOffset = 0; + assert(StructTy->getNumElements() < 4 && "Unknown wide pointer layout"); + for (unsigned i = 0; i < StructTy->getNumElements(); ++i) { + llvm::DIType *DElemTy = CreateMemberType(Unit, ElemQTy, MemberNames[i], &FieldOffset); + EltTys.push_back(DElemTy); + } + + llvm::DICompositeType *DStructTy = DBuilder.createStructType( + Unit, StructTy->getName(), nullptr, 0, CGM.getContext().getTypeSize(Ty), + CGM.getContext().getTypeAlign(Ty), llvm::DINode::FlagZero, nullptr, + DBuilder.getOrCreateArray(EltTys)); + + return DStructTy; + } else + /* TO_UPSTREAM(BoundsSafety) OFF*/ return CreatePointerLikeType(llvm::dwarf::DW_TAG_pointer_type, Ty, Ty->getPointeeType(), Unit); } @@ -1495,6 +1519,53 @@ static llvm::DINode::DIFlags getAccessFlag(AccessSpecifier Access, llvm_unreachable("unexpected access enumerator"); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +llvm::DIType *CGDebugInfo::CreateType(const CountAttributedType *Ty, + llvm::DIFile *Unit) { + QualType DesugaredTy = Ty->desugar(); + llvm::DIType *DesugaredDITy = getOrCreateType(DesugaredTy, Unit); + SourceLocation Loc; + + // Map the pointer type into a typedef with a recognizable name so that + // debuggers may provide special formatters for them. + std::string TypedefName; + if (Ty->isCountInBytes() && Ty->isOrNull()) + TypedefName = "__bounds_safety::sized_by_or_null::"; + else if (Ty->isCountInBytes()) + TypedefName = "__bounds_safety::sized_by::"; + else if (Ty->isOrNull()) + TypedefName = "__bounds_safety::counted_by_or_null::"; + else + TypedefName = "__bounds_safety::counted_by::"; + llvm::raw_string_ostream ExprStream(TypedefName); + Ty->getCountExpr()->printPretty( + ExprStream, nullptr, PrintingPolicy(CGM.getContext().getLangOpts())); + return DBuilder.createTypedef(DesugaredDITy, TypedefName, Unit, + getLineNumber(Loc), Unit); +} + +llvm::DIType *CGDebugInfo::CreateType(const DynamicRangePointerType *Ty, + llvm::DIFile *Unit) { + QualType DesugaredTy = Ty->desugar(); + llvm::DIType *DesugaredDITy = getOrCreateType(DesugaredTy, Unit); + SourceLocation Loc; + + // Map the pointer type into a typedef with a recognizable name so that + // debuggers may provide special formatters for them. + std::string TypedefName = "__bounds_safety::dynamic_range::"; + llvm::raw_string_ostream ExprStream(TypedefName); + if (auto *StartExpr = Ty->getStartPointer()) + StartExpr->printPretty(ExprStream, nullptr, + PrintingPolicy(CGM.getContext().getLangOpts())); + ExprStream << "::"; + if (auto *ExndExpr = Ty->getEndPointer()) + ExndExpr->printPretty(ExprStream, nullptr, + PrintingPolicy(CGM.getContext().getLangOpts())); + return DBuilder.createTypedef(DesugaredDITy, TypedefName, Unit, + getLineNumber(Loc), Unit); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::DIType *CGDebugInfo::CreateType(const TypedefType *Ty, llvm::DIFile *Unit) { llvm::DIType *Underlying = @@ -3621,10 +3692,17 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { case Type::Attributed: T = cast(T)->getEquivalentType(); break; + case Type::ValueTerminated: + T = cast(T)->desugar(); + break; case Type::BTFTagAttributed: T = cast(T)->getWrappedType(); break; case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (C.getLangOpts().BoundsSafety) + return C.getQualifiedType(T.getTypePtr(), Quals); + /* TO_UPSTREAM(BoundsSafety) OFF*/ T = cast(T)->desugar(); break; case Type::Elaborated: @@ -3823,7 +3901,14 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::TemplateSpecialization: return CreateType(cast(Ty), Unit); + /* TO_UPSTREAM(BoundsSafety) ON */ case Type::CountAttributed: + if (CGM.getLangOpts().BoundsSafety) + return CreateType(cast(Ty), Unit); + break; + case Type::DynamicRangePointer: + return CreateType(cast(Ty), Unit); + /* TO_UPSTREAM(BoundsSafety) OFF */ case Type::Auto: case Type::Attributed: case Type::BTFTagAttributed: @@ -3840,6 +3925,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::Decltype: case Type::PackIndexing: case Type::UnaryTransform: + case Type::ValueTerminated: break; } diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index a0c419cf1e208..0bcfa9ddd87a6 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -192,6 +192,10 @@ class CGDebugInfo { llvm::DIType *CreateType(const PointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const BlockPointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const FunctionType *Ty, llvm::DIFile *F); + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::DIType *CreateType(const CountAttributedType *Ty, llvm::DIFile *F); + llvm::DIType *CreateType(const DynamicRangePointerType *Ty, llvm::DIFile *F); + /* TO_UPSTREAM(BoundsSafety) OFF */ /// Get structure or union type. llvm::DIType *CreateType(const RecordType *Tyg); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 39330853ce705..dd90a370f4fd0 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -16,6 +16,7 @@ #include "CGDebugInfo.h" #include "CGOpenCLRuntime.h" #include "CGOpenMPRuntime.h" +#include "CGRecordLayout.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" @@ -770,12 +771,13 @@ static void drillIntoBlockVariable(CodeGenFunction &CGF, lvalue.setAddress(CGF.emitBlockByrefAddress(lvalue.getAddress(), var)); } -void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, +// TO_UPSTREAM(BoundsSafety): LHSQTy +void CodeGenFunction::EmitNullabilityCheck(QualType LHSQTy, llvm::Value *RHS, SourceLocation Loc) { if (!SanOpts.has(SanitizerKind::NullabilityAssign)) return; - auto Nullability = LHS.getType()->getNullability(); + auto Nullability = LHSQTy->getNullability(); if (!Nullability || *Nullability != NullabilityKind::NonNull) return; @@ -784,13 +786,20 @@ void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, SanitizerScope SanScope(this); llvm::Value *IsNotNull = Builder.CreateIsNotNull(RHS); llvm::Constant *StaticData[] = { - EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHS.getType()), + EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHSQTy), llvm::ConstantInt::get(Int8Ty, 0), // The LogAlignment info is unused. llvm::ConstantInt::get(Int8Ty, TCK_NonnullAssign)}; EmitCheck({{IsNotNull, SanitizerKind::NullabilityAssign}}, SanitizerHandler::TypeMismatch, StaticData, RHS); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, + SourceLocation Loc) { + EmitNullabilityCheck(LHS.getType(), RHS, Loc); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, LValue lvalue, bool capturedByInit) { Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); @@ -1885,6 +1894,119 @@ void CodeGenFunction::emitZeroOrPatternForAutoVarInit(QualType type, } } +/* TO_UPSTREAM(BoundsSafety) ON */ +static void boundsSafetyZeroInit(CodeGenFunction &CGF, const Address Addr, + const bool IsVolatileQualified) { + llvm::Type *ElTy = Addr.getElementType(); + llvm::Constant *constant = constWithPadding( + CGF.CGM, IsPattern::No, llvm::Constant::getNullValue(ElTy)); + + auto *Ty = constant->getType(); + uint64_t ConstantSize = CGF.CGM.getDataLayout().getTypeAllocSize(Ty); + if (!ConstantSize) + return; + + bool canDoSingleStore = Ty->isIntOrIntVectorTy() || + Ty->isPtrOrPtrVectorTy() || Ty->isFPOrFPVectorTy(); + if (canDoSingleStore) { + auto *I = CGF.Builder.CreateStore(constant, Addr, IsVolatileQualified); + I->addAnnotationMetadata("bounds-safety-zero-init"); + } else { + auto *SizeVal = llvm::ConstantInt::get(CGF.CGM.IntPtrTy, ConstantSize); + auto *I = CGF.Builder.CreateMemSet( + Addr, llvm::ConstantInt::get(CGF.CGM.Int8Ty, 0), SizeVal, + IsVolatileQualified); + I->addAnnotationMetadata("bounds-safety-zero-init"); + } +} + +template +static auto makeCachingAddressInitializer(T &Action, std::optional
&Addr) { + return [&]() { + if (!Addr) { + Addr = Action(); + } + return *Addr; + }; +} + +static void boundsSafetyRecursiveInit(CodeGenFunction &CGF, const Decl *D, + const QualType Ty, bool IsVolatileQualified, + std::function BaseAddrGen) { + const DependerDeclsAttr *Att = D ? D->getAttr() : nullptr; + auto ShouldInitializeType = [](QualType Ty) -> bool { + return Ty->isSafePointerType() || Ty->isCountAttributedType() || + Ty->isDynamicRangePointerType() || Ty->isValueTerminatedType(); + }; + // declarations referred to from _sized_by etc attributes + if (Att && Att->dependerDecls_size() > 0) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + } else if (ShouldInitializeType(Ty)) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + } else if (auto RTy = Ty->getAs()) { + auto RD = RTy->getDecl(); + assert(RD); + if (RD->isUnion()) { + for (auto F : RD->fields()) { + auto FTy = F->getType(); + if (ShouldInitializeType(FTy)) { + // zero-init the whole record + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + break; + } + } + } else { + for (auto F : RD->fields()) { + if (F->isZeroSize(CGF.getContext())) + continue; + + std::optional
NewBase; + auto FieldAddrGenAction = [&]() { + const unsigned FieldIdx = + CGF.CGM.getTypes().getCGRecordLayout(RD).getLLVMFieldNo(F); + return CGF.Builder.CreateStructGEP( + BaseAddrGen(), FieldIdx, F->getName()); + }; + auto FieldAddrGen = + makeCachingAddressInitializer(FieldAddrGenAction, NewBase); + boundsSafetyRecursiveInit(CGF, F, F->getType(), + IsVolatileQualified || F->getType().isVolatileQualified(), + FieldAddrGen); + } + } + } else if (auto CATy = dyn_cast(Ty)) { + auto ElemTy = CATy->getElementType(); + if (ShouldInitializeType(ElemTy)) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), + IsVolatileQualified || ElemTy.isVolatileQualified()); + } else { + const uint64_t Size = CATy->getSize().getZExtValue(); + llvm::Constant *ZeroIdx = llvm::ConstantInt::get(CGF.IntTy, 0); + + for (uint64_t Idx = 0; Idx < Size; ++Idx) { + std::optional
NewBase; + auto ElemAddrGenAction = [&]() { + Address BaseAddr = BaseAddrGen(); + auto *CIIdx = llvm::ConstantInt::get(CGF.IntPtrTy, Idx); + CharUnits offset = dyn_cast(CIIdx)->getZExtValue() * + CGF.getContext().getTypeSizeInChars(ElemTy); + CharUnits ElemAlignment = + BaseAddr.getAlignment().alignmentAtOffset(offset); + return CGF.Builder.CreateGEP(BaseAddr, {ZeroIdx, CIIdx}, + CGF.ConvertTypeForMem(ElemTy), + ElemAlignment, ""); + }; + auto ElemAddrGen = + makeCachingAddressInitializer(ElemAddrGenAction, NewBase); + boundsSafetyRecursiveInit(CGF, nullptr, ElemTy, + IsVolatileQualified || ElemTy.isVolatileQualified(), + ElemAddrGen); + } + } + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { assert(emission.Variable && "emission was not valid!"); @@ -1932,6 +2054,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { const Address Loc = locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (!Init && CGM.getLangOpts().BoundsSafety) { + auto ReturnLoc = [&]() { return Loc; }; + boundsSafetyRecursiveInit(*this, &D, D.getType(), + D.getType().isVolatileQualified(), ReturnLoc); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Note: constexpr already initializes everything correctly. LangOptions::TrivialAutoVarInitKind trivialAutoVarInit = (D.isConstexpr() diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 1b67720a3250a..978389314d8d7 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -22,6 +22,8 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -69,6 +71,222 @@ static llvm::cl::opt ClSanitizeGuardChecks( // Miscellaneous Helper Methods //===--------------------------------------------------------------------===// +llvm::Value *CodeGenFunction::GetWidePointerElement(Address Addr, + WPIndex Index) { + auto GetElemStr = [&]() { + switch (Index) { + case WPIndex::Pointer: + return "ptr"; + case WPIndex::Upper: + return "ub"; + case WPIndex::Lower: + return "lb"; + } + }; + + std::string Name = "wide_ptr."; + Name += GetElemStr(); + Address ElemAddr = + Builder.CreateStructGEP(Addr, (unsigned int)Index, Name + ".addr"); + return Builder.CreateLoad(ElemAddr, Name); +} + +void CodeGenFunction::EmitPtrCastLECheck(llvm::Value *LHS, llvm::Value *RHS, + BoundsSafetyTrapKind TrapKind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + // TODO(dliew): rdar://109574814: opt-remarks could be added here. + { + BoundsSafetyOptRemarkScope Scope(this, GetBoundsSafetyOptRemarkForTrap(TrapKind)); + llvm::Value *Check = Builder.CreateICmpULE(LHS, RHS); + EmitBoundsSafetyTrapCheck(Check, TrapKind, TrapCtx); + } +} + +void CodeGenFunction::EmitBoundsSafetyBoundsCheck( + llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower, bool AcceptNullPtr, + BoundsSafetyTrapCtx::Kind TrapCtx) { + if (!Upper && !Lower) + return; + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + + // rdar://109574814: opt-remarks could be added here. + llvm::BranchInst *NullCheckBranch = nullptr; + if (AcceptNullPtr) { + // Test that the pointer is not NULL before testing that it's in bounds, + // if AcceptNullPtr is specified. + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_NEQ_NULL); + llvm::BasicBlock *NotNull = createBasicBlock("boundscheck.notnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrIsNull = Builder.CreateICmpNE(Ptr, Null); + NullCheckBranch = Builder.CreateCondBr(PtrIsNull, NotNull, nullptr); + EmitBlock(NotNull); + } + + if (Upper) { + if (getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_AccessSize) && + ElemTy->isSized()) { + // For sized types take the size of a potential access into account. + // For something like: + // + // T* ptr; + // + // We check + // 1. `ptr.ptr + sizeof(T)` does not overflow + // 2. `ptr.ptr + sizeof(T) <= ptr.upper` + // + assert(ElemTy); + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GT_UPPER_BOUND); + llvm::Value *OnePastTheEndPtr = + Builder.CreateGEP(ElemTy, Ptr, llvm::ConstantInt::get(SizeTy, 1)); + // Emitting the upper bound check first since it's more + // optimization-friendly. This is because the upper bound calculation (ptr + // + size) is often marked 'inbounds' if 'ptr' is '__counted_by' or an + // array decay of a sized array. This allows ConstraintElimination to use + // this information to infer subsequent pointer arithmetic (ptr + i; where + // 'i <= size') doesn't wrap. + EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(OnePastTheEndPtr, Upper), + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(Ptr, OnePastTheEndPtr), + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + } else { + // Legacy path and path for unsized types (e.g. function types). + // In this case for something like + // + // T* ptr; + // + // We assume that `sizeof(T)` is 1. In this case + // + // `ptr.ptr + sizeof(T) <= ptr.upper` simplifies to + // `ptr.ptr < ptr.upper`. + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND); + llvm::Value *Check = Builder.CreateICmpULT(Ptr, Upper); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx); + } + } + + if (Lower) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND); + llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx); + } + if (NullCheckBranch) + NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock()); +} + +void CodeGenFunction::EmitBoundsSafetyRangeCheck(llvm::Value *LowerBound, + llvm::Value *LowerAccess, + llvm::Value *UpperAccess, + llvm::Value *UpperBound, + BoundsSafetyTrapCtx::Kind TrapCtx) { + // rdar://109574814: opt-remarks could be added here. + + // LowerBound <= LowerAccess <= UpperAccess <= UpperBound. + // This function assumes that LowerAccess <= UpperAccess and + // LowerBound <= UpperBound, so in practice it only checks + // LowerBound <= LowerAccess && UpperAccess <= UpperBound. + if (UpperAccess != UpperBound) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND); + llvm::Value *UpperCheck = Builder.CreateICmpULE(UpperAccess, UpperBound); + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + EmitBoundsSafetyTrapCheck(UpperCheck, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx); + } + + if (LowerBound != LowerAccess) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND); + llvm::Value *LowerCheck = Builder.CreateICmpULE(LowerBound, LowerAccess); + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + EmitBoundsSafetyTrapCheck(LowerCheck, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx); + } +} + +llvm::Value *CodeGenFunction::EmitWideToRawPtr(const Expr *E, bool BoundsCheck, + BoundsSafetyTrapCtx::Kind TrapCtx, + bool LowerOnlyCheck, + llvm::Value **UpperPtr, + llvm::Value **LowerPtr, + bool IsNullAllowed) { + auto PT = E->getType()->getAs(); + assert(PT && !PT->hasRawPointerLayout() && "Expected wide pointer type"); + RValue PtrRV = EmitAnyExpr(E); + Address PtrAddress = PtrRV.getAggregateAddress(); + llvm::Value *Ptr = GetWidePointerElement(PtrAddress, WPIndex::Pointer); + llvm::Value *Upper = GetWidePointerElement(PtrAddress, WPIndex::Upper); + llvm::Value *Lower = PT->isBidiIndexable() + ? GetWidePointerElement(PtrAddress, WPIndex::Lower) + : nullptr; + // XXX: Lower bound check only for wide to counted_by + if (BoundsCheck || LowerOnlyCheck) + EmitBoundsSafetyBoundsCheck(ConvertTypeForMem(PT->getPointeeType()), Ptr, + LowerOnlyCheck ? nullptr : Upper, Lower, + IsNullAllowed, TrapCtx); + if (UpperPtr) + *UpperPtr = Upper; + if (LowerPtr) + *LowerPtr = Lower; + return Ptr; +} + +llvm::Value *CodeGenFunction::EmitTerminator(const llvm::APSInt &Terminator, + llvm::Type *Ty) { + if (Terminator.isZero()) + return llvm::Constant::getNullValue(Ty); + if (Ty->isPointerTy()) { + auto *IntPtrTy = CGM.getDataLayout().getIntPtrType(Ty); + auto *C = llvm::ConstantInt::get(IntPtrTy, Terminator.getSExtValue()); + return Builder.CreateIntToPtr(C, Ty); + } + if (Terminator.isSigned()) + return llvm::ConstantInt::getSigned(Ty, Terminator.getSExtValue()); + return llvm::ConstantInt::get(Ty, Terminator.getZExtValue()); +} + +void CodeGenFunction::EmitValueTerminatedPointerArithmeticCheck( + QualType PointerType, llvm::Value *Ptr) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_TERMINATED_BY_PTR_ARITH); + assert(PointerType->isPointerType()); + QualType PointeeType = PointerType->getPointeeType(); + + CharUnits Alignment = getContext().getTypeAlignInChars(PointeeType); + Address PtrAddr = Address(Ptr, ConvertTypeForMem(PointeeType), Alignment); + llvm::Value *Val = Builder.CreateLoad(PtrAddr); + + const auto *VTT = PointerType->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(getContext()); + llvm::Value *Term = EmitTerminator(TermVal, Val->getType()); + + llvm::Value *Check = Builder.CreateICmpNE(Val, Term); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_PTR_ARITH); +} + +void CodeGenFunction::EmitValueTerminatedAssignmentCheck( + const BinaryOperator *E, LValue Value) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_TERMINATED_BY_TERM_ASSIGN); + const Expr *LHS = E->getLHS()->IgnoreParenCasts(); + const Expr *PtrE = nullptr; + if (const auto *UnOp = dyn_cast(LHS)) { + if (UnOp->getOpcode() == UO_Deref) + PtrE = UnOp->getSubExpr(); + } else if (const auto *ASE = dyn_cast(LHS)) { + PtrE = ASE->getBase(); + } + if (!PtrE) + return; + + const auto *VTT = PtrE->getType()->getAs(); + if (!VTT) + return; + + llvm::Value *Val = EmitLoadOfLValue(Value, E->getExprLoc()).getScalarVal(); + + llvm::APSInt TermVal = VTT->getTerminatorValue(getContext()); + llvm::Value *Term = EmitTerminator(TermVal, Val->getType()); + + llvm::Value *Check = Builder.CreateICmpNE(Val, Term); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_TERM_ASSIGN); +} + /// CreateTempAlloca - This creates a alloca and inserts it into the entry /// block. RawAddress @@ -191,6 +409,15 @@ llvm::Value *CodeGenFunction::EvaluateExprAsBool(const Expr *E) { QualType BoolTy = getContext().BoolTy; SourceLocation Loc = E->getExprLoc(); CGFPOptionsRAII FPOptsRAII(*this, E); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto *PT = E->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) + return EmitScalarConversion( + EmitWideToRawPtr(E), getContext().getPointerType(PT->getPointeeType()), + BoolTy, Loc); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (!E->getType()->isAnyComplexType()) return EmitScalarConversion(EmitScalarExpr(E), E->getType(), BoolTy, Loc); @@ -1202,6 +1429,162 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, SanitizerHandler::OutOfBounds, StaticData, Index); } +static BoundsSafetyTrapCtx::Kind +GetTrapCtxFromPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *E) { + switch (E->getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + return BoundsSafetyTrapCtx::ASSIGN; + case BoundsCheckKind::FlexibleArrayCountCast: + return BoundsSafetyTrapCtx::CAST; + case BoundsCheckKind::FlexibleArrayCountDeref: + return BoundsSafetyTrapCtx::DEREF; + } + llvm_unreachable("Unhandled BoundsCheckKind"); +} + +static llvm::Value *getPtrSubDivisor(CodeGenFunction &CGF, + QualType elementType) { + assert(!elementType->isVariableArrayType()); + CharUnits elementSize; + // Handle GCC extension for pointer arithmetic on void* and + // function pointer types. + if (elementType->isVoidType() || elementType->isFunctionType()) + elementSize = CharUnits::One(); + else + elementSize = CGF.getContext().getTypeSizeInChars(elementType); + + // Don't even emit the divide for element size of 1. + if (elementSize.isOne()) + return nullptr; + + return CGF.CGM.getSize(elementSize); +} + +/// @brief This function performs the check to ensure `Count <= Upper - FamPtr` +/// and other bounds checks related to the count and the pointers, e.g., checks +/// to ensure `Count >= 0` and `Upper >= FamPtr`. +void CodeGenFunction::EmitFlexibleArrayCountCheck( + const PredefinedBoundsCheckExpr *E) { + const Expr *BasePtr = E->getFamBasePtr(); + const Expr *FamPtr = E->getFamPtr(); + const Expr *Count = E->getFamCount(); + + assert(BasePtr && FamPtr && Count); + + llvm::Value *Upper; + llvm::Value *Lower; + BoundsSafetyTrapCtx::Kind TrapCtx = GetTrapCtxFromPredefinedBoundsCheckExpr(E); + // Necessary bounds checks are performed later. + llvm::Value *Ptr = EmitWideToRawPtr(BasePtr, /*BoundsCheck*/ false, TrapCtx, + /*LowerOnlyCheck*/ false, &Upper, &Lower); + + llvm::BranchInst *NullCheckBranch = nullptr; + // Deref doesn't survive with null. + if (E->getKind() != BoundsCheckKind::FlexibleArrayCountDeref) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_NEQ_NULL); + + llvm::BasicBlock *NonnullBB = createBasicBlock("flex.base.nonnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrNonnull = + Builder.CreateICmpNE(Ptr, Null, "flex.base.null.check"); + NullCheckBranch = Builder.CreateCondBr(PtrNonnull, NonnullBB, nullptr); + EmitBlock(NonnullBB); + } + + // `FamPtr` is essentially `Ptr + FamOffset` and we need an overflow check + // for this calculation. Evaluating `FamPtr` would be emitted as StructGEP + // which always gets `inbounds` which will make `Ptr <= Ptr + FamOffset` be + // considered true. Hence we do the pointer arithmetic without `inbouds` here. + // We don't have the exact FamOffset here so we actually do `Ptr <= Ptr + + // sizeof(*Ptr)` instead but this should cover the overflow check because + // `sizeof(*Ptr) <= FamOffset`. + llvm::Type *BaseElemTy = + ConvertTypeForMem(BasePtr->getType()->getPointeeType()); + llvm::Constant *One = llvm::ConstantInt::get(SizeTy, 1); + llvm::Value *PtrPlusSize = Builder.CreateGEP(BaseElemTy, Ptr, One, ""); + EmitPtrCastLECheck(Ptr, PtrPlusSize, BNS_TRAP_PTR_PAST_END_OVERFLOW); + + // XXX: Some redundant bounds checks are performed on the struct base pointer + // in `EmitMemberExpr` in order to emit FamPtr. This is not a big problem + // because redundant checks go away in optimized builds so we leave it as is + // for now. rdar://104875616 + llvm::Value *FamPtrVal = EmitScalarExpr(FamPtr); + llvm::Value *UserProvidedCount = EmitScalarExpr(Count); + + // Ensure `Count >= 0`. + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_COUNT_NEGATIVE); + llvm::Value *Zero = llvm::ConstantInt::get(UserProvidedCount->getType(), 0); + llvm::Value *ZeroLE = + Builder.CreateICmpSLE(Zero, UserProvidedCount, "flex.count.minus"); + EmitBoundsSafetyTrapCheck(ZeroLE, BNS_TRAP_COUNT_NEGATIVE, TrapCtx); + } + + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, + BNS_OR_FLEX_COUNT_GT_BOUNDS); + + // Ensure `FamPtr (Ptr + FamOffset) <= Upper`. + // This is necessary because we later do `Upper - FamPtr` to get the + // avaiable count. + EmitPtrCastLECheck(FamPtrVal, Upper, BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + + // Ensure Lower <= Ptr. + EmitBoundsSafetyBoundsCheck(nullptr, Ptr, nullptr, Lower, + /*AcceptNullPtr*/ false, TrapCtx); + + QualType ElemTy = FamPtr->getType()->getPointeeType(); + + llvm::Value *UpperIntPtr = + Builder.CreatePtrToInt(Upper, PtrDiffTy, "upper.intptr"); + llvm::Value *FamIntPtr = + Builder.CreatePtrToInt(FamPtrVal, PtrDiffTy, "fam.intptr"); + // `diffInChars = Upper - FamPtrVal`. We can do this subtraction because + // we ensure `FamPtrVal <= Upper` in the previous bounds check in the + // function. + llvm::Value *diffInChars = Builder.CreateSub( + UpperIntPtr, FamIntPtr, "flex.avail.count", /*HasNUW*/ true); + llvm::Value *PointerDerivedCount = diffInChars; + // `PointerDerivedCount = diffInChars / sizeof(Fam[0])`. In other words, we + // get the element count by dividing the byte count with the element type + // size. + if (llvm::Value *divisor = getPtrSubDivisor(*this, ElemTy)) { + PointerDerivedCount = + Builder.CreateExactSDiv(diffInChars, divisor, "flex.avail.count.div"); + } + assert(PointerDerivedCount->getType() == IntPtrTy); + // It's safe to zero extend the count because we previously ensured `Count + // >= 0` in this function. + UserProvidedCount = + Builder.CreateIntCast(UserProvidedCount, IntPtrTy, + /*isSigned*/ false, "flex.count.intptr"); + // Ensure `Count <= PointerDerivedCount (Upper - FamPtr)`. + llvm::Value *CountCheck = Builder.CreateICmpULE( + UserProvidedCount, PointerDerivedCount, "flex.count.check"); + EmitBoundsSafetyTrapCheck(CountCheck, BNS_TRAP_FLEX_COUNT_GT_BOUNDS); + } + + if (NullCheckBranch) + NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock()); +} + +void CodeGenFunction::EmitBoundsSafetyTrapCheck(const Expr *Cond, + BoundsSafetyTrapKind kind) { + auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); + assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); + llvm::Value *CondVal = EvaluateExprAsBool(Cond); + + // The result of `EvaluateExprAsBool()` might be a phi instruction + // which is not created via `Builder` which means BoundsSafetyOptRemarkScope + // won't annotate it. Annotate the instruction explicitly. + if (auto *I = llvm::dyn_cast(CondVal)) { + I->addAnnotationMetadata(GetBoundsSafetyOptRemarkString(OptRemark)); + } + + EmitBoundsSafetyTrapCheck(CondVal, kind); +} + CodeGenFunction::ComplexPairTy CodeGenFunction:: EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre) { @@ -1254,6 +1637,8 @@ void CodeGenModule::EmitExplicitCastExprType(const ExplicitCastExpr *E, static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo, + llvm::Value **UpperPtr, + llvm::Value **LowerPtr, KnownNonNull_t IsKnownNonNull, CodeGenFunction &CGF) { // We allow this with ObjC object pointers because of fragile ABIs. @@ -1278,7 +1663,8 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, LValueBaseInfo InnerBaseInfo; TBAAAccessInfo InnerTBAAInfo; Address Addr = CGF.EmitPointerWithAlignment( - CE->getSubExpr(), &InnerBaseInfo, &InnerTBAAInfo, IsKnownNonNull); + CE->getSubExpr(), &InnerBaseInfo, &InnerTBAAInfo, UpperPtr, + LowerPtr, IsKnownNonNull); if (BaseInfo) *BaseInfo = InnerBaseInfo; if (TBAAInfo) *TBAAInfo = InnerTBAAInfo; @@ -1308,6 +1694,58 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, CE->getBeginLoc()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + if (auto DestPtTy = E->getType()->getAs()) { + if (!DestPtTy->hasRawPointerLayout()) { + QualType ETy = DestPtTy->getPointeeType(); + llvm::Type *DestPointeeType = CGF.ConvertTypeForMem(ETy); + if (DestPointeeType->isVoidTy()) + DestPointeeType = llvm::Type::getInt8Ty(CGF.getLLVMContext()); + + unsigned AS = DestPointeeType->isFunctionTy() + ? CGF.CGM.getDataLayout().getProgramAddressSpace() + : CGF.getTypes().getTargetAddressSpace(ETy); + llvm::Type *DestTy = llvm::PointerType::get(DestPointeeType, AS); + + auto RemoveBoundsSafetyAttrs = [&] (const QualType& QT) { + if (auto PtrTy = QT->getAs()) { + if (!PtrTy->hasRawPointerLayout()) { + const auto SrcPointeeTy = PtrTy->getPointeeType(); + const auto SrcPtrRawTy = + CGF.CGM.getContext().getPointerType(SrcPointeeTy); + return QualType(SrcPtrRawTy.getTypePtr(), QT.getQualifiers().getAsOpaqueValue()); + } + } + return QT; + }; + + const QualType DestPtrTyForAuth = RemoveBoundsSafetyAttrs(CE->getType()); + const QualType SrcPtrTyForAuth = RemoveBoundsSafetyAttrs(CE->getSubExpr()->getType()); + + auto CastBoundPtr = [&CGF, &DestTy, &SrcPtrTyForAuth, + &DestPtrTyForAuth, &CE](llvm::Value **Bound) { + if (Bound && *Bound) { + *Bound = CE->getCastKind() != CK_AddressSpaceConversion + ? CGF.Builder.CreateBitCast(*Bound, DestTy) + : CGF.Builder.CreateAddrSpaceCast(*Bound, DestTy); + *Bound = CGF.authPointerToPointerCast(*Bound, SrcPtrTyForAuth, + DestPtrTyForAuth); + } + }; + + CastBoundPtr(UpperPtr); + CastBoundPtr(LowerPtr); + + Address Result = + CE->getCastKind() != CK_AddressSpaceConversion + ? Addr.withElementType(DestPointeeType) + : CGF.Builder.CreateAddrSpaceCast(Addr, DestTy, + DestPointeeType); + return CGF.authPointerToPointerCast( + Result, CE->getSubExpr()->getType(), CE->getType()); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF */ llvm::Type *ElemTy = CGF.ConvertTypeForMem(E->getType()->getPointeeType()); Addr = Addr.withElementType(ElemTy); @@ -1332,7 +1770,7 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, if (TBAAInfo) *TBAAInfo = CGF.CGM.getTBAAAccessInfo(E->getType()); Address Addr = CGF.EmitPointerWithAlignment( - CE->getSubExpr(), BaseInfo, nullptr, + CE->getSubExpr(), BaseInfo, nullptr, nullptr, nullptr, (KnownNonNull_t)(IsKnownNonNull || CE->getCastKind() == CK_UncheckedDerivedToBase)); auto Derived = CE->getSubExpr()->getType()->getPointeeCXXRecordDecl(); @@ -1376,9 +1814,20 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, // TODO: conditional operators, comma. + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Extract the pointer from wide pointer + auto PT = E->getType()->getAs(); + llvm::Value *Base = + (PT && !PT->hasRawPointerLayout()) + ? CGF.EmitWideToRawPtr(E, /*BoundsCheck=*/false, + /*TrapCtx=*/BoundsSafetyTrapCtx::UNKNOWN, + /*LowerOnlyCheck=*/false, UpperPtr, LowerPtr) + : CGF.EmitScalarExpr(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // Otherwise, use the alignment of the type. return CGF.makeNaturalAddressForPointer( - CGF.EmitScalarExpr(E), E->getType()->getPointeeType(), CharUnits(), + Base, E->getType()->getPointeeType(), CharUnits(), /*ForPointeeType=*/true, BaseInfo, TBAAInfo, IsKnownNonNull); } @@ -1386,9 +1835,10 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, /// derive a more accurate bound on the alignment of the pointer. Address CodeGenFunction::EmitPointerWithAlignment( const Expr *E, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo, + llvm::Value **UpperPtr, llvm::Value **LowerPtr, KnownNonNull_t IsKnownNonNull) { - Address Addr = - ::EmitPointerWithAlignment(E, BaseInfo, TBAAInfo, IsKnownNonNull, *this); + Address Addr = ::EmitPointerWithAlignment(E, BaseInfo, TBAAInfo, UpperPtr, + LowerPtr, IsKnownNonNull, *this); if (IsKnownNonNull && !Addr.isKnownNonNull()) Addr.setKnownNonNull(); return Addr; @@ -1467,7 +1917,12 @@ bool CodeGenFunction::IsWrappedCXXThis(const Expr *Obj) { LValue CodeGenFunction::EmitCheckedLValue(const Expr *E, TypeCheckKind TCK) { LValue LV; - if (SanOpts.has(SanitizerKind::ArrayBounds) && isa(E)) + auto *ASE = dyn_cast(E); + if (ASE && (SanOpts.has(SanitizerKind::ArrayBounds) || + /*TO_UPSTREAM(BoundsSafety) ON*/ + + ASE->getBase()->getType()->isPointerTypeWithBounds())) + /*TO_UPSTREAM(BoundsSafety) OFF*/ LV = EmitArraySubscriptExpr(cast(E), /*Accessed*/true); else LV = EmitLValue(E); @@ -1613,6 +2068,22 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E, CXXDefaultInitExprScope Scope(*this, DIE); return EmitLValue(DIE->getExpr(), IsKnownNonNull); } + + case Expr::BoundsCheckExprClass: + return EmitBoundsCheckExprLValue(cast(E)); + case Expr::PredefinedBoundsCheckExprClass: + return EmitPredefinedBoundsCheckExprLValue( + cast(E)); + case Expr::AssumptionExprClass: + return EmitAssumptionExprLValue(cast(E)); + case Expr::ForgePtrExprClass: + return EmitForgePtrExprLValue(cast(E)); + case Expr::BoundsSafetyPointerPromotionExprClass: + return EmitBoundsSafetyPointerPromotionExprLValue( + cast(E)); + case Expr::MaterializeSequenceExprClass: + return EmitMaterializeSequenceExprLValue(cast(E)); + case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast(E)); @@ -3237,9 +3708,19 @@ LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; - Address Addr = EmitPointerWithAlignment(E->getSubExpr(), &BaseInfo, - &TBAAInfo); - LValue LV = MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo); + /*TO_UPSTREAM(BoundsSafety) ON*/ + LValue LV; + const auto *PT = E->getSubExpr()->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) { + llvm::Value *Ptr = EmitWideToRawPtr(E->getSubExpr(), /*BoundsCheck=*/true, + /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF); + LV = MakeAddrLValue(Ptr, T, getContext().getTypeAlignInChars(T)); + } else { + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Address Addr = + EmitPointerWithAlignment(E->getSubExpr(), &BaseInfo, &TBAAInfo); + LV = MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo); + } LV.getQuals().setAddressSpace(ExprTy.getAddressSpace()); // We should not generate __weak write barrier on indirect reference @@ -3252,6 +3733,13 @@ LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) { LV.setNonGC(!E->isOBJCGCCandidate(getContext())); return LV; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + case UO_AddrOf: { + if (!E->getType()->isPointerTypeWithBounds()) + llvm_unreachable("Unknown unary operator lvalue!"); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) ON*/ case UO_Real: case UO_Imag: { LValue LV = EmitLValue(E->getSubExpr()); @@ -3860,7 +4348,11 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { } void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, - SanitizerHandler CheckHandlerID) { + SanitizerHandler CheckHandlerID, + /*TO_UPSTREAM(BoundsSafety) ON*/ + StringRef Annotation, + StringRef TrapMessage) { + /*TO_UPSTREAM(BoundsSafety) OFF*/ llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -3870,40 +4362,107 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; + /*TO_UPSTREAM(BoundsSafety) ON*/ + llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); + if (CheckHandlerID == SanitizerHandler::BoundsSafety && getDebugInfo()) { + TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( + TrapLocation, GetBoundsSafetyTrapMessagePrefix(), TrapMessage); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (!ClSanitizeDebugDeoptimization && CGM.getCodeGenOpts().OptimizationLevel && TrapBB && - (!CurCodeDecl || !CurCodeDecl->hasAttr())) { + (!CurCodeDecl || !CurCodeDecl->hasAttr()) && + /*TO_UPSTREAM(BoundsSafety) ON*/ + !CGM.getCodeGenOpts().TrapFuncReturns && + !CGM.getCodeGenOpts().UniqueTrapBlocks) { + /*TO_UPSTREAM(BoundsSafety) OFF*/ auto Call = TrapBB->begin(); assert(isa(Call) && "Expected call in trap BB"); - Call->applyMergedLocation(Call->getDebugLoc(), - Builder.getCurrentDebugLocation()); - Builder.CreateCondBr(Checked, Cont, TrapBB); + Call->applyMergedLocation(Call->getDebugLoc(), TrapLocation); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Merge the debug info on the unreachable too so its debug info is not + // stale. + auto Unreachable = ++TrapBB->begin(); + assert(isa(Unreachable) && + "Expected unreachable instruction in trap BB"); + Unreachable->applyMergedLocation(Unreachable->getDebugLoc(), TrapLocation); + + if (!Annotation.empty()) { + Call->addAnnotationMetadata(Annotation); + Unreachable->addAnnotationMetadata(Annotation); + } + auto *CondBrInst = Builder.CreateCondBr(Checked, Cont, TrapBB); + if (!Annotation.empty()) + CondBrInst->addAnnotationMetadata(Annotation); + /* TO_UPSTREAM(BoundsSafety) OFF*/ } else { - TrapBB = createBasicBlock("trap"); - Builder.CreateCondBr(Checked, Cont, TrapBB); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (CGM.getCodeGenOpts().UniqueTrapBlocks && + !CGM.getCodeGenOpts().TrapFuncReturns) + TrapBB = createUnmergeableBasicBlock("trap"); + else + TrapBB = createBasicBlock("trap"); + auto *BrInst = Builder.CreateCondBr(Checked, Cont, TrapBB); + if (!Annotation.empty()) + BrInst->addAnnotationMetadata(Annotation); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + EmitBlock(TrapBB); - llvm::CallInst *TrapCall = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::ubsantrap), - llvm::ConstantInt::get(CGM.Int8Ty, - ClSanitizeDebugDeoptimization - ? TrapBB->getParent()->size() - : static_cast(CheckHandlerID))); - - if (!CGM.getCodeGenOpts().TrapFuncName.empty()) { - auto A = llvm::Attribute::get(getLLVMContext(), "trap-func-name", - CGM.getCodeGenOpts().TrapFuncName); - TrapCall->addFnAttr(A); + /*TO_UPSTREAM(BoundsSafety) ON*/ + ApplyDebugLocation applyTrapDI(*this, TrapLocation); + llvm::CallInst *TrapCall; + if (CGM.getCodeGenOpts().TrapFuncReturns) { + auto *TrapID = llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID); + llvm::FunctionType *FnType = + llvm::FunctionType::get(CGM.VoidTy, {TrapID->getType()}, false); + auto TrapFunc = CGM.CreateRuntimeFunction( + FnType, CGM.getCodeGenOpts().TrapFuncName); + TrapCall = EmitNounwindRuntimeCall(TrapFunc, {TrapID}); + Builder.CreateBr(Cont); + } else { + auto *TrapID = llvm::ConstantInt::get( + CGM.Int8Ty, (ClSanitizeDebugDeoptimization && + !CGM.getCodeGenOpts().UniqueTrapBlocks) + ? TrapBB->getParent()->size() + : static_cast(CheckHandlerID)); + TrapCall = Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::ubsantrap), TrapID); + if (!CGM.getCodeGenOpts().TrapFuncName.empty()) { + auto A = llvm::Attribute::get(getLLVMContext(), "trap-func-name", + CGM.getCodeGenOpts().TrapFuncName); + TrapCall->addFnAttr(A); + } + TrapCall->setDoesNotReturn(); + auto Unreachable = Builder.CreateUnreachable(); + if (!Annotation.empty()) + Unreachable->addAnnotationMetadata(Annotation); } - TrapCall->setDoesNotReturn(); + if (!Annotation.empty()) + TrapCall->addAnnotationMetadata(Annotation.str()); TrapCall->setDoesNotThrow(); - Builder.CreateUnreachable(); } + /*TO_UPSTREAM(BoundsSafety) OFF*/ EmitBlock(Cont); } +void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked, + BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); + assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); + + // We still need to pass `OptRemark` because not all emitted instructions + // can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck + // caches basic blocks that contain instructions that need annotating. + EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, + GetBoundsSafetyOptRemarkString(OptRemark), + GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx)); +} + llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { llvm::CallInst *TrapCall = Builder.CreateCall(CGM.getIntrinsic(IntrID)); @@ -3919,7 +4478,9 @@ llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, LValueBaseInfo *BaseInfo, - TBAAAccessInfo *TBAAInfo) { + TBAAAccessInfo *TBAAInfo, + // TO_UPSTREAM(BoundsSafety) + TBAAAccessInfo *EltTBAAInfo) { assert(E->getType()->isArrayType() && "Array to pointer decay must have array source type!"); @@ -3948,6 +4509,10 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, QualType EltType = E->getType()->castAsArrayTypeUnsafe()->getElementType(); if (BaseInfo) *BaseInfo = LV.getBaseInfo(); if (TBAAInfo) *TBAAInfo = CGM.getTBAAAccessInfo(EltType); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (EltTBAAInfo) + *EltTBAAInfo = CGM.getTBAAInfoForSubobject(LV, EltType); + /* TO_UPSTREAM(BoundsSafety) OFF*/ return Addr.withElementType(ConvertTypeForMem(EltType)); } @@ -4137,6 +4702,116 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, return Address(eltPtr, CGF.ConvertTypeForMem(eltType), eltAlign); } +Address CodeGenFunction::EmitArrayToWidePointerDecay( + const Expr *E, llvm::Value *&Upper, LValueBaseInfo *BaseInfo, + TBAAAccessInfo *TBAAInfo, TBAAAccessInfo *EltTBAAInfo) { + assert(E->getType()->isArrayType()); + Address Addr = EmitArrayToPointerDecay(E, BaseInfo, TBAAInfo, EltTBAAInfo); + llvm::Value *Ptr = Addr.getBasePointer(); + llvm::Value *Count = nullptr; + QualType OrigSrcTy = E->getType(); + QualType SrcTy = OrigSrcTy.getDesugaredType(getContext()); + if (auto CAT = dyn_cast(SrcTy)) + Count = llvm::ConstantInt::get(getLLVMContext(), CAT->getSize()); + else if (auto VAT = dyn_cast(SrcTy)) + Count = getVLASize(VAT).NumElts; + else if (isa(SrcTy)) { + if (const auto *DCPTy = OrigSrcTy->getAs()) { + const Expr *CountExpr = DCPTy->getCountExpr(); + assert(!isa(E->IgnoreParenCasts())); + Count = EmitScalarExpr(CountExpr); + assert(CountExpr->getType()->isIntegralOrEnumerationType()); + Count = CountExpr->getType()->isSignedIntegerOrEnumerationType() + ? Builder.CreateSExt(Count, IntPtrTy) + : Builder.CreateZExt(Count, IntPtrTy); + + // Propagate the assumption that unsigned count is non-negative because + // the optimizer doesn't have the concept of signess. + if (CountExpr->getType()->isUnsignedIntegerOrEnumerationType()) + Builder.CreateAssumption(Builder.CreateICmpSGE( + Count, llvm::Constant::getNullValue(Count->getType()), + "count.positive")); + } else + Count = llvm::ConstantInt::get(IntPtrTy, 0); + } else + llvm_unreachable("Unexpected array type"); + + // We assume here that the upper bounds have been checked for overflow at + // initialization. + Upper = Builder.CreateInBoundsGEP(Addr.getElementType(), Ptr, Count, "upper"); + + return Addr; +} + +LValue +CodeGenFunction::EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E, + bool Accessed) { + const auto *PT = E->getBase()->getType()->getAs(); + assert(PT && !PT->hasRawPointerLayout()); + + LValueBaseInfo BaseInfo; + TBAAAccessInfo TBAAInfo; + llvm::Value *Upper = nullptr; + llvm::Value *Lower = nullptr; + Address WidePtrAddr = Address::invalid(); + Address Addr = Address::invalid(); + QualType ArrayTy{}; + if (auto *Array = isSimpleArrayDecayOperand(E->getBase())) { + ArrayTy = Array->getType(); + // We need the element's TBAA info for array subscript, not the array's + // TBAA. + Addr = EmitArrayToWidePointerDecay(Array, Upper, &BaseInfo, + /*TBAAInfo*/ nullptr, + /*EltTBAAInfo*/ &TBAAInfo); + Lower = Addr.getBasePointer(); + } else { + RValue BaseRV = EmitAnyExpr(E->getBase()); + WidePtrAddr = BaseRV.getAggregateAddress(); + Addr = makeNaturalAddressForPointer( + GetWidePointerElement(WidePtrAddr, WPIndex::Pointer), E->getType(), + CGM.getNaturalTypeAlignment(E->getType(), &BaseInfo, &TBAAInfo)); + } + + llvm::Value *Idx = EmitScalarExpr(E->getIdx()); + QualType IdxTy = E->getIdx()->getType(); + bool IdxSigned = IdxTy->isSignedIntegerOrEnumerationType(); + + // Extend or truncate the index type to 32 or 64-bits. + if (Idx->getType() != IntPtrTy) + Idx = Builder.CreateIntCast(Idx, IntPtrTy, IdxSigned, "idxprom"); + + // For VLAs, the memory type resolves to the innermost element type. + // Hence, multiplying by the VLA size emulates the behavior of GEP over + // a constant array. + if (const VariableArrayType *vla = + getContext().getAsVariableArrayType(E->getType())) { + llvm::Value *numElements = getVLASize(vla).NumElts; + // Do not use `CreateNSWMul` because With -fbounds-safety GEP doesn't + // get 'inbounds' by default. So, this also emulates the behavior of + // GEP on a constant array indexed with a non-constant value. + Idx = Builder.CreateMul(Idx, numElements); + } + + QualType *ArrayTyPtr = ArrayTy.isNull() ? nullptr : &ArrayTy; + Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(), + /*inbounds*/ false, IdxSigned, E->getExprLoc(), + ArrayTyPtr, E->getBase()); + if (Accessed) { + // __indexable pointers should have been cast to __bidi_indexable in Sema. + assert(E->getBase()->getType()->isBidiIndexablePointerType()); + if (WidePtrAddr.isValid()) { + Upper = GetWidePointerElement(WidePtrAddr, WPIndex::Upper); + Lower = GetWidePointerElement(WidePtrAddr, WPIndex::Lower); + } + assert(!!Upper && !!Lower); + llvm::Type *ElemTy = ConvertTypeForMem(E->getType()); + EmitBoundsSafetyBoundsCheck(ElemTy, Addr.getBasePointer(), Upper, Lower, + /*AcceptNullPtr=*/false, + /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF); + } + return MakeAddrLValue(Addr, E->getType(), BaseInfo, TBAAInfo); +} + /// The offset of a field from the beginning of the record. static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD, const FieldDecl *FD, int64_t &Offset) { @@ -4194,6 +4869,9 @@ static std::optional getOffsetDifferenceInBits(CodeGenFunction &CGF, LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, bool Accessed) { + const auto *PT = E->getBase()->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) + return EmitWidePtrArraySubscriptExpr(E, Accessed); // The index must always be an integer, which is not an aggregate. Emit it // in lexical order (this complexity is, sadly, required by C++17). llvm::Value *IdxPre = @@ -4686,9 +5364,23 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { // If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar. LValue BaseLV; if (E->isArrow()) { + llvm::Value *Upper = nullptr; + llvm::Value *Lower = nullptr; LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; - Address Addr = EmitPointerWithAlignment(BaseExpr, &BaseInfo, &TBAAInfo); + Address Addr = EmitPointerWithAlignment(BaseExpr, &BaseInfo, &TBAAInfo, + &Upper, &Lower); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (Upper) { + llvm::Value *Ptr = Addr.getBasePointer(); + llvm::Value *OnePastTheEndPtr = Builder.CreateGEP(Addr.getElementType(), Ptr, + llvm::ConstantInt::get(SizeTy, 1)); + EmitBoundsSafetyRangeCheck(Lower ? Lower : Ptr, Ptr, OnePastTheEndPtr, Upper, + BoundsSafetyTrapCtx::DEREF); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + QualType PtrTy = BaseExpr->getType()->getPointeeType(); SanitizerSet SkippedChecks; bool IsBaseCXXThis = IsWrappedCXXThis(BaseExpr); @@ -5237,13 +5929,7 @@ LValue CodeGenFunction::EmitConditionalOperatorLValue( LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { switch (E->getCastKind()) { case CK_ToVoid: - case CK_BitCast: - case CK_LValueToRValueBitCast: - case CK_ArrayToPointerDecay: case CK_FunctionToPointerDecay: - case CK_NullToMemberPointer: - case CK_NullToPointer: - case CK_IntegralToPointer: case CK_PointerToIntegral: case CK_PointerToBoolean: case CK_IntegralCast: @@ -5285,6 +5971,18 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_HLSLArrayRValue: return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_LValueToRValueBitCast: + case CK_ArrayToPointerDecay: + case CK_NullToMemberPointer: + case CK_NullToPointer: + case CK_IntegralToPointer: { + if (!E->getType()->isPointerTypeWithBounds()) + return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!"); @@ -5329,6 +6027,22 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { return LV; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BitCast: { + auto PT = E->getType()->getAs(); + if (PT->hasRawPointerLayout()) + return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + return EmitAggExprToLValue(E); + } + + case CK_BoundsSafetyPointerCast: { + auto PT = E->getType()->getAs(); + if (PT->hasRawPointerLayout()) + return EmitLValue(E->getSubExpr()); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { const auto *DerivedClassTy = @@ -5767,6 +6481,10 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { E->getOpcode() == BO_PtrMemI) return EmitPointerToDataMemberBinaryExpr(E); + if (E->getType()->isPointerTypeWithBounds()) { + return EmitAggExprToLValue(E); + } + assert(E->getOpcode() == BO_Assign && "unexpected binary l-value"); // Note that in all of these cases, __block variables need the RHS @@ -5903,6 +6621,79 @@ CodeGenFunction::EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E) { return MakeAddrLValue(Slot.getAddress(), E->getType(), AlignmentSource::Decl); } +LValue CodeGenFunction::EmitBoundsCheckExprLValue(const BoundsCheckExpr *E) { + const auto *BoundsCheck = cast(E); + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : BoundsCheck->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(*this, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + + { + RAIIDisableUBSanChecks DisableChecks(*this); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + EmitBoundsSafetyTrapCheck(BoundsCheck->getCond(), BNS_TRAP_GENERAL); + } + LValue Res = EmitLValue(BoundsCheck->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(*this); + } + return Res; +} + +LValue CodeGenFunction::EmitPredefinedBoundsCheckExprLValue( + const PredefinedBoundsCheckExpr *E) { + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitMaterializeSequenceExprLValue( + const MaterializeSequenceExpr *MSE) { + if (MSE->isBinding()) { + for (auto *OVE : MSE->opaquevalues()) { + if (CodeGenFunction::OpaqueValueMappingData::shouldBindAsLValue(OVE)) { + RValue PtrRV = EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(*this, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind( + *this, OVE, OVE->getSourceExpr()); + } + } + } + + LValue LV = EmitLValue(MSE->getWrappedExpr()); + + if (MSE->isUnbinding()) { + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(*this, OVE); + } + + return LV; +} + +LValue CodeGenFunction::EmitBoundsSafetyPointerPromotionExprLValue( + const BoundsSafetyPointerPromotionExpr *E) { + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitForgePtrExprLValue(const ForgePtrExpr *E) { + if (!E->getType()->isPointerTypeWithBounds()) + return EmitUnsupportedLValue(E, "l-value expression"); + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitAssumptionExprLValue(const AssumptionExpr *E) { + llvm::Function *FnAssume = CGM.getIntrinsic(llvm::Intrinsic::assume); + for (auto *Assumption : E->assumptions()) { + Builder.CreateCall(FnAssume, EvaluateExprAsBool(Assumption)); + } + return EmitLValue(E->getWrappedExpr()); +} + LValue CodeGenFunction::EmitObjCMessageExprLValue(const ObjCMessageExpr *E) { RValue RV = EmitObjCMessageExpr(E); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index d9f44f4be617e..f932eab06f52f 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -12,6 +12,7 @@ #include "CGCXXABI.h" #include "CGObjCRuntime.h" +#include "CGRecordLayout.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" @@ -146,6 +147,23 @@ class AggExprEmitter : public StmtVisitor { void VisitDeclRefExpr(DeclRefExpr *E) { EmitAggLoadOfLValue(E); } void VisitMemberExpr(MemberExpr *ME) { EmitAggLoadOfLValue(ME); } void VisitUnaryDeref(UnaryOperator *E) { EmitAggLoadOfLValue(E); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitUnaryAddrOf(UnaryOperator *E); + void VisitUnaryPostDec(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/true, /*IsPre*/false); + } + void VisitUnaryPostInc(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/false, /*IsPre*/false); + } + void VisitUnaryPreDec(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/true, /*IsPre*/true); + } + void VisitUnaryPreInc(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/false, /*IsPre*/true); + } + void EmitWidePtrPrePostIncDec(UnaryOperator *E, bool IsSub, bool IsPre); + /* TO_UPSTREAM(BoundsSafety) OFF */ + void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); } void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); void VisitArraySubscriptExpr(ArraySubscriptExpr *E) { @@ -161,9 +179,13 @@ class AggExprEmitter : public StmtVisitor { void VisitStmtExpr(const StmtExpr *E); void VisitBinaryOperator(const BinaryOperator *BO); void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *BO); + // TO_UPSTREAM(BoundsSafety) + void VisitBoundPointerArithmetic(const BinaryOperator *BO); void VisitBinAssign(const BinaryOperator *E); void VisitBinComma(const BinaryOperator *E); void VisitBinCmp(const BinaryOperator *E); + // TO_UPSTREAM(BoundsSafety) + void VisitCompoundAssignOperator(CompoundAssignOperator *E); void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) { Visit(E->getSemanticForm()); } @@ -226,6 +248,20 @@ class AggExprEmitter : public StmtVisitor { void VisitCXXParenListOrInitListExpr(Expr *ExprToVisit, ArrayRef Args, Expr *ArrayFiller); + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitGetBoundExpr(GetBoundExpr *E) { + CGF.EmitAggExpr(E->getSubExpr(), Dest); + if (Dest.isIgnored()) + return; + Address Addr = Dest.getAddress(); + WPIndex SrcIndex = E->getBoundKind() == GetBoundExpr::BK_Lower + ? WPIndex::Lower : WPIndex::Upper; + llvm::Value *Component = CGF.GetWidePointerElement(Addr, SrcIndex); + Address DstAddr = Builder.CreateStructGEP(Addr, (unsigned)WPIndex::Pointer); + Builder.CreateStore(Component, DstAddr); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void EmitInitializationToLValue(Expr *E, LValue Address); void EmitNullInitializationToLValue(LValue Address); // case Expr::ChooseExprClass: @@ -237,6 +273,86 @@ class AggExprEmitter : public StmtVisitor { void VisitPackIndexingExpr(PackIndexingExpr *E) { Visit(E->getSelectedExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: handle wide pointer operations. + using WidePointerElemCallback = std::function; + static WidePointerElemCallback DefaultElemCallback; + + void EmitWidePointer( + LValue DestLV, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower = nullptr, + WidePointerElemCallback PtrElemCallback = DefaultElemCallback); + void EmitWidePointerToDest( + QualType DestTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower = nullptr, + WidePointerElemCallback PtrElemCallback = DefaultElemCallback) { + // Skip it if the dest is ignored. + if (Dest.isIgnored()) + return; + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), DestTy); + return EmitWidePointer(DestLV, Ptr, Upper, Lower, PtrElemCallback); + } + void EmitBoundsSafetyPointerConversion(CastExpr *E); + void EmitNullToBoundPointer(CastExpr *E); + void EmitArrayToBoundPointerDecay(CastExpr *E); + void EmitWidePointerBitCast(CastExpr *E); + + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, llvm::Value *Idx, + QualType IdxType, bool IsSub = false); + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, const Expr *Idx, + QualType IdxType, bool IsSub = false); + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, unsigned int Idx, + ASTContext &Ctx, bool IsSub) { + llvm::Value *Index = llvm::ConstantInt::get(CGF.Int32Ty, Idx); + QualType Int32Ty = Ctx.getIntTypeForBitwidth(32, true); + + EmitCheckedBoundPointerArithmetic(DestLV, E, Base, Index, Int32Ty, IsSub); + } + + void EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub); + void EmitBoundPointerArithmetic(LValue DestLV, LValue BaseLV, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub); + void EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, const Expr *Idx, + bool IsSub = false) { + llvm::Value *Index = CGF.EmitAnyExpr(Idx).getScalarVal(); + const bool IsSigned = Idx->getType()->isSignedIntegerOrEnumerationType(); + + EmitBoundPointerArithmetic(DestLV, Base, Index, IsSigned, IsSub); + } + void VisitAssumptionExpr(AssumptionExpr *E); + void VisitForgePtrExpr(ForgePtrExpr *E); + void VisitBoundsCheckExpr(BoundsCheckExpr *E) { + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : E->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(CGF, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + { + RAIIDisableUBSanChecks DisableChecks(CGF); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + CGF.EmitBoundsSafetyTrapCheck(E->getCond(), BNS_TRAP_GENERAL); + } + Visit(E->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(CGF); + } + } + void VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *E); + void VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E); + + void VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE); + + void VisitTerminatedByToIndexableExpr(TerminatedByToIndexableExpr *E); + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace. @@ -244,11 +360,19 @@ class AggExprEmitter : public StmtVisitor { // Utilities //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON */ +AggExprEmitter::WidePointerElemCallback AggExprEmitter::DefaultElemCallback = + [](llvm::Value *V) { return V; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// EmitAggLoadOfLValue - Given an expression with aggregate type that /// represents a value lvalue, this method emits the address of the lvalue, /// then loads the result into DestPtr. void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { - LValue LV = CGF.EmitLValue(E); + /*TO_UPSTREAM(BoundsSafety) ON*/ + LValue LV = E->getType()->isPointerTypeWithBounds() ? + CGF.EmitCheckedLValue(E, CodeGenFunction::TCK_Load) : CGF.EmitLValue(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ // If the type of the l-value is atomic, then do an atomic load. if (LV.getType()->isAtomicType() || CGF.LValueIsSuitableForInlineAtomic(LV)) { @@ -259,6 +383,546 @@ void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { EmitFinalDestCopy(E->getType(), LV); } +/*TO_UPSTREAM(BoundsSafety) ON*/ +void AggExprEmitter::EmitNullToBoundPointer(CastExpr *E) { + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + EmitNullInitializationToLValue(DestLV); +} + +void AggExprEmitter::EmitArrayToBoundPointerDecay(CastExpr *E) { + assert(E->getType()->isPointerTypeWithBounds() && + E->getType()->getAs()->isBidiIndexable()); + + llvm::Value *Upper = nullptr; + Address Addr = CGF.EmitArrayToWidePointerDecay(E->getSubExpr(), Upper); + llvm::Value *Ptr = Addr.getBasePointer(); + EmitWidePointerToDest(E->getType(), Ptr, Upper, Ptr); +} + +void AggExprEmitter::EmitWidePointerBitCast(CastExpr *E) { + QualType RawPointerTy = + CGF.getContext().getPointerType(E->getType()->getPointeeType()); + + WidePointerElemCallback ElemBitCast = + [&](llvm::Value *PtrVal) -> llvm::Value * { + return Builder.CreateBitCast(PtrVal, CGF.ConvertType(RawPointerTy)); + }; + + RValue SrcRV = CGF.EmitAnyExpr(E->getSubExpr()); + assert(SrcRV.isAggregate()); + Address SrcAddr = SrcRV.getAggregateAddress(); + EmitWidePointerToDest(E->getType(), + CGF.GetWidePointerElement(SrcAddr, WPIndex::Pointer), + CGF.GetWidePointerElement(SrcAddr, WPIndex::Upper), + E->getType()->isBidiIndexablePointerType() + ? CGF.GetWidePointerElement(SrcAddr, WPIndex::Lower) + : nullptr, + ElemBitCast); +} + +void AggExprEmitter::EmitWidePointer(LValue DestLV, llvm::Value *Ptr, + llvm::Value *Upper, llvm::Value *Lower, + WidePointerElemCallback PtrElemCallback) { + + auto *PT = DestLV.getType()->getAs(); + unsigned Idx = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address Addr = Builder.CreateStructGEP(DestLV.getAddress(), Idx++); + Builder.CreateStore(PtrElemCallback(Val), Addr); + }; + + // Initialize ptr field of the wide pointer record + InitPtrField(Ptr); + + if (PT->getPointerAttributes().hasUpperBound()) + InitPtrField(Upper); + + // Initialize lower bound if exist + if (PT->getPointerAttributes().hasLowerBound()) { + assert(Lower); + InitPtrField(Lower); + } +} + +void AggExprEmitter::VisitForgePtrExpr(ForgePtrExpr *E) { + RValue AddrRV = CGF.EmitAnyExpr(E->getAddr()); + llvm::Value *Ptr; + if (AddrRV.isScalar()) + Ptr = AddrRV.getScalarVal(); + else { + assert(AddrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(AddrRV.getAggregateAddress(), WPIndex::Pointer); + } + llvm::Value *ByteSize = CGF.EmitScalarExpr(E->getSize()); + // XXX: bitcast can be removed upon the full transition to opaque pointer types + // (rdar://92073883). + Ptr = Builder.CreateBitOrPointerCast(Ptr, CGF.VoidPtrTy); + QualType PointeeType = E->getType()->getPointeeType(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(PointeeType); + llvm::Value *Upper = Builder.CreateGEP(ElemTy, Ptr, ByteSize); + llvm::Type *RawPtrTy = + CGF.ConvertTypeForMem(CGF.getContext().getPointerType(PointeeType)); + WidePointerElemCallback Callback = [&](llvm::Value *V) -> llvm::Value * { + return Builder.CreateBitCast(V, RawPtrTy); + }; + EmitWidePointerToDest(E->getType(), Ptr, Upper, Ptr, Callback); +} + +void AggExprEmitter::VisitAssumptionExpr(AssumptionExpr *E) { + llvm::Function *FnAssume = CGF.CGM.getIntrinsic(llvm::Intrinsic::assume); + for (Expr *Assumption : E->assumptions()) { + Builder.CreateCall(FnAssume, CGF.EvaluateExprAsBool(Assumption)); + } + return Visit(E->getWrappedExpr()); +} + +void AggExprEmitter:: +VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + llvm::Value *Ptr; + RValue PtrRV = CGF.EmitAnyExpr(E->getPointer()); + if (PtrRV.isScalar()) + Ptr = PtrRV.getScalarVal(); + else { + assert(PtrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(PtrRV.getAggregateAddress(), WPIndex::Pointer); + } + + llvm::BranchInst *NullCheckBranch = nullptr; + if (E->getNullCheck()) { + // Test that the pointer is not NULL before testing that it's in bounds, + // if AcceptNullPtr is specified. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_PTR_NEQ_NULL); + llvm::BasicBlock *NotNull = CGF.createBasicBlock("boundscheck.notnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrIsNull = Builder.CreateICmpNE(Ptr, Null); + NullCheckBranch = Builder.CreateCondBr(PtrIsNull, NotNull, nullptr); + CGF.EmitBlock(NotNull); + } + + llvm::Value *Lower; + if (auto *LowerE = E->getLowerBound()) { + RValue LowerRV = CGF.EmitAnyExpr(LowerE); + if (LowerRV.isScalar()) + Lower = LowerRV.getScalarVal(); + else { + assert(LowerRV.isAggregate()); + Lower = CGF.GetWidePointerElement(LowerRV.getAggregateAddress(), WPIndex::Pointer); + } + } else { + Lower = Ptr; + } + + llvm::Value *Upper; + { + RAIIDisableUBSanChecks DisableUBSanChecks(CGF); + RValue UpperRV = CGF.EmitAnyExpr(E->getUpperBound()); + if (UpperRV.isScalar()) + Upper = UpperRV.getScalarVal(); + else { + assert(UpperRV.isAggregate()); + Upper = CGF.GetWidePointerElement(UpperRV.getAggregateAddress(), + WPIndex::Pointer); + } + } + + auto *PT = E->getType()->getAs(); + llvm::Type *RawPtrTy = + CGF.ConvertType(CGF.getContext().getPointerType(PT->getPointeeType())); + WidePointerElemCallback Callback = [&](llvm::Value *V) -> llvm::Value * { + return Builder.CreateBitCast(V, RawPtrTy); + }; + EmitWidePointerToDest(E->getType(), Ptr, Upper, Lower, Callback); + + // Creating the following control flow graph. + // nullcheck: + // br cond nonnull, null + // nonnull: + // promotion operations + // br cont + // null: + // fill result with null + // br cont + // cont: + if (NullCheckBranch) { + llvm::BasicBlock *ContBlock = CGF.createBasicBlock("boundscheck.cont"); + Builder.CreateBr(ContBlock); + + llvm::BasicBlock *NullBlock = CGF.createBasicBlock("boundscheck.null"); + NullCheckBranch->setSuccessor(1, NullBlock); + CGF.EmitBlock(NullBlock); + llvm::Value *Zero = llvm::Constant::getNullValue(Ptr->getType()); + EmitWidePointerToDest(E->getType(), Zero, Zero, Zero, Callback); + CGF.EmitBlock(ContBlock); + } +} + +void AggExprEmitter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + CGF.EmitFlexibleArrayCountCheck(E); + Visit(E->getGuardedExpr()); +} + +void AggExprEmitter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE) { + if (MSE->isBinding()) { + for (auto *OVE : MSE->opaquevalues()) { + if (CodeGenFunction::OpaqueValueMappingData::shouldBindAsLValue(OVE)) { + RValue PtrRV = CGF.EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = CGF.MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, OVE->getSourceExpr()); + } + } + } + + Visit(MSE->getWrappedExpr()); + + if (MSE->isUnbinding()) { + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(CGF, OVE); + } +} + +static llvm::Value *EmitTerminatedByToIndexableStrlen(CodeGenFunction &CGF, + RValue PtrRV, + QualType PointeeTy, + SourceLocation Loc) { + ASTContext &C = CGF.getContext(); + + const char *Name; + unsigned BuiltinID; + QualType CTy; + if (PointeeTy->isCharType()) { + Name = "__builtin_strlen"; + BuiltinID = Builtin::BI__builtin_strlen; + CTy = C.CharTy; + } else { + Name = "__builtin_wcslen"; + BuiltinID = Builtin::BI__builtin_wcslen; + CTy = C.WCharTy; + } + + const IdentifierInfo *II = &C.Idents.get(Name); + + QualType ReturnTy = C.getSizeType(); + QualType StrTy = CTy; + StrTy.addConst(); + StrTy = C.getPointerType(StrTy); + SmallVector ArgTys{StrTy}; + QualType FunctionTy = C.getFunctionType(ReturnTy, ArgTys, {}); + + FunctionDecl *FD = FunctionDecl::Create( + C, C.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, + FunctionTy, /*TInfo=*/nullptr, SC_Extern, /*UsesFPIntrin=*/false, + /*hasWrittenPrototype=*/false); + + auto *Parm = ParmVarDecl::Create( + C, FD, SourceLocation(), SourceLocation(), nullptr, StrTy, + C.getTrivialTypeSourceInfo(StrTy, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + FD->setParams({Parm}); + + llvm::Constant *FnPtr = CGF.CGM.getBuiltinLibFunction(FD, BuiltinID); + CGCallee Callee = CGCallee::forDirect(FnPtr, GlobalDecl(FD)); + + CallArgList Args; + Args.add(PtrRV, StrTy); + const CGFunctionInfo &FnInfo = CGF.CGM.getTypes().arrangeBuiltinFunctionCall( + ReturnTy, Args); + + llvm::CallBase *CallOrInvoke = nullptr; + RValue Call = + CGF.EmitCall(FnInfo, Callee, ReturnValueSlot(), Args, &CallOrInvoke, + /*IsMustTail=*/false, Loc); + return Call.getScalarVal(); +} + +static llvm::Value * +EmitTerminatedByToIndexableLoop(CodeGenFunction &CGF, RValue PtrRV, + QualType PointeeTy, + const llvm::APSInt &TermVal) { + ASTContext &C = CGF.getContext(); + auto &Builder = CGF.Builder; + + llvm::Value *Ptr = PtrRV.getScalarVal(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(PointeeTy); + + llvm::BasicBlock *Cond = CGF.createBasicBlock("terminated_by.loop_cond"); + llvm::BasicBlock *Cont = CGF.createBasicBlock("terminated_by.loop_cont"); + llvm::BasicBlock *End = CGF.createBasicBlock("terminated_by.loop_end"); + + RawAddress LenAllocaAddr = + CGF.CreateTempAlloca(CGF.SizeTy, CGF.getSizeAlign(), "terminated_by.len"); + llvm::Constant *Null = llvm::Constant::getNullValue(CGF.SizeTy); + Builder.CreateStore(Null, LenAllocaAddr); + + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + + CGF.EmitBlock(Cond); + + llvm::Value *Len = Builder.CreateLoad(LenAllocaAddr, "terminated_by.len"); + llvm::Value *GEP = Builder.CreateInBoundsGEP(ElemTy, Ptr, Len); + Address ElemAddr = Address(GEP, ElemTy, C.getTypeAlignInChars(PointeeTy)); + llvm::Value *Elem = Builder.CreateLoad(ElemAddr, "terminated_by.elem"); + llvm::Value *TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminted_by.check_terminator"); + Builder.CreateCondBr(TermCheck, End, Cont); + + CGF.EmitBlock(Cont); + + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + llvm::Value *NewLen = Builder.CreateAdd(Len, One, "terminated_by.new_len"); + Builder.CreateStore(NewLen, LenAllocaAddr); + Builder.CreateBr(Cond); + + CGF.EmitBlock(End); + + return Builder.CreateLoad(LenAllocaAddr); +} + +void AggExprEmitter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + ASTContext &C = CGF.getContext(); + + RValue PtrRV = CGF.EmitAnyExpr(E->getPointer()); + assert(PtrRV.isScalar()); + llvm::Value *Ptr = PtrRV.getScalarVal(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(E->getType()->getPointeeType()); + + const auto *VTT = E->getPointer()->getType()->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(C); + + // Arrays are decayed to pointers, thus we expect a pointer here. + assert(VTT->isPointerType()); + QualType PointeeTy = VTT->getPointeeType(); + + // Try to call a builtin to get the length, if available. If not, we emit a + // loop to get the position of the terminator. + llvm::Value *Length; + if (!CGF.getLangOpts().Freestanding && TermVal.isZero() && + (PointeeTy->isCharType() || PointeeTy->isWideCharType())) { + Length = EmitTerminatedByToIndexableStrlen(CGF, PtrRV, PointeeTy, + E->getExprLoc()); + } else { + Length = EmitTerminatedByToIndexableLoop(CGF, PtrRV, PointeeTy, TermVal); + } + + if (E->includesTerminator()) { + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + Length = Builder.CreateAdd(Length, One); + } + + llvm::Value *Upper = + Builder.CreateInBoundsGEP(ElemTy, Ptr, Length, "terminated_by.upper"); + EmitWidePointerToDest(E->getType(), Ptr, Upper, /*Lower=*/Ptr); +} + +void AggExprEmitter::VisitUnaryAddrOf(UnaryOperator *E) { + // Get bounds of underlying object + Expr *SubExpr = E->getSubExpr()->IgnoreParens(); + + // C99 6.5.3.2p3, Address and indirection operators + // [...] If the operand is the result of a unary * operator, neither + // that operator nor the & operator is evaluated and the result is as + // if both were omitted, except that the constraints on the operators + // still apply and the result is not an lvalue. + // In BoundsSafety, this translates into that bounds of '&*ptr' should be + // the same as those of 'ptr'. + int32_t DerefLevel = 0; + Expr *UnderlyingSubExpr = SubExpr; + while (auto *UO = dyn_cast(UnderlyingSubExpr)) { + bool StopSubExpr = false; + switch (UO->getOpcode()) { + case UO_Deref: + DerefLevel++; + break; + case UO_AddrOf: + DerefLevel--; + break; + default: + StopSubExpr = true; + break; + } + if (StopSubExpr) + break; + UnderlyingSubExpr = UO->getSubExpr()->IgnoreParens(); + } + if (DerefLevel == 1 && UnderlyingSubExpr->IgnoreImpCasts()->isLValue()) { + return Visit(UnderlyingSubExpr); + } + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + // Array subscripts, member expressions are common enough that we have a + // peephole. However, we bail out for rarer cases that we handle better by + // taking the address of EmitLValue. + if (auto *ASE = dyn_cast(SubExpr)) + return EmitCheckedBoundPointerArithmetic( + DestLV, ASE, ASE->getBase(), ASE->getIdx(), ASE->getIdx()->getType()); + + // In the case of member expressions, we only peephole struct field accesses; + // unions go through the inefficient path for now. + if (auto *ME = dyn_cast(SubExpr)) { + auto *Member = dyn_cast(ME->getMemberDecl()); + if (Member && Member->getParent()->getTagKind() == TagTypeKind::Struct) { + auto Base = ME->getBase(); + llvm::Value *Ptr = nullptr; + RecordDecl *Rec = cast(Member->getParent()); + unsigned MemberIdx = + CGF.CGM.getTypes().getCGRecordLayout(Rec).getLLVMFieldNo(Member); + if (ME->isArrow()) { + if (Base->getType()->getAs()->hasRawPointerLayout()) { + Ptr = CGF.EmitScalarExpr(Base); + } else { + Ptr = CGF.EmitWideToRawPtr( + Base, /*BoundsCheck=*/true, + /*BoundsSafetyTrapCtx=*/BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER); + } + llvm::Value *GepIndices[] = { + Builder.getInt32(0), Builder.getInt32(MemberIdx) + }; + llvm::Type *BaseStructTy = + CGF.ConvertTypeForMem(Base->getType()->getPointeeType()); + Ptr = Builder.CreateInBoundsGEP(BaseStructTy, Ptr, GepIndices); + } else { + LValue BaseLV = CGF.EmitLValue(Base); + Ptr = Builder.CreateStructGEP(BaseLV.getAddress(), MemberIdx) + .getBasePointer(); + } + + QualType PointeeType = E->getType()->getPointeeType(); + QualType RawPointerType = CGF.getContext().getPointerType(PointeeType); + llvm::Type *PointeeTypeForMem = CGF.ConvertTypeForMem(PointeeType); + llvm::ConstantInt *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + // We always add `inbounds` to create upper bound for addr of member + // access. This is safe because if the base is any safe pointer (__single, + // __bidi_indexable, etc.), the preceding member access being successfully + // executed means the base is in bounds and it's safe to emit gep + // inbounds. And if the base is __unsafe_indexable, it's already unsafe + // and we don't care about bounds checking being optimized. If the base is + // not a pointer, this is essentially address of a single object (i.e., + // &s.member) and should also be in bounds. + llvm::Value *Upper = + Builder.CreateInBoundsGEP(PointeeTypeForMem, Ptr, One); + + return EmitWidePointer(DestLV, Ptr, Upper, Ptr, [&](llvm::Value *PtrVal) { + // XXX: bitcast can be removed upon the full transition to opaque pointer types + // (rdar://92073883). + return Builder.CreateBitCast(PtrVal, + CGF.ConvertTypeForMem(RawPointerType)); + }); + } + } + + LValue SubExprLV = CGF.EmitLValue(SubExpr); + llvm::Value *Ptr = SubExprLV.getPointer(CGF); + llvm::Type *ElemTy = SubExprLV.getAddress().getElementType(); + llvm::Value *Upper = Builder.CreateGEP(ElemTy, Ptr, + llvm::ConstantInt::get(CGF.SizeTy, 1)); + EmitWidePointer(DestLV, Ptr, Upper, /*Lower*/ Ptr); +} + +void AggExprEmitter::EmitWidePtrPrePostIncDec(UnaryOperator *E, bool IsSub, bool IsPre) { + assert(E->getType()->isPointerTypeWithBounds()); + assert((!IsSub || E->getType()->isBidiIndexablePointerType()) && + "decrease for array type cannot pass the semantic check"); + LValue LV = CGF.EmitLValue(E->getSubExpr()); + if (!IsPre) + EmitFinalDestCopy(E->getType(), LV); + + EmitCheckedBoundPointerArithmetic(LV, E, E->getSubExpr(), 1, CGF.getContext(), + IsSub); + if (IsPre) + EmitFinalDestCopy(E->getType(), LV); +} + +void AggExprEmitter::EmitBoundsSafetyPointerConversion(CastExpr *E) { + const PointerType *DstTy = E->getType()->getAs(); + const PointerType *SrcTy = E->getSubExpr()->getType()->getAs(); + + assert(DstTy); + assert(SrcTy); + assert(DstTy->getPointerAttributes() != SrcTy->getPointerAttributes()); + assert(CGF.getContext().hasSameType(DstTy->getPointeeType(), + SrcTy->getPointeeType())); + assert(DstTy->isIndexable() || DstTy->isBidiIndexable()); + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + unsigned Idx = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address Addr = Builder.CreateStructGEP(DestLV.getAddress(), Idx++); + // For instance, this may be true when an end pointer has a different + // type than its start pointer. + if (Val->getType() != Addr.getElementType()) + Val = Builder.CreateBitOrPointerCast(Val, Addr.getElementType()); + Builder.CreateStore(Val, Addr); + }; + + llvm::Value *Ptr, *Upper; + if (SrcTy->hasRawPointerLayout()) { + Ptr = CGF.EmitScalarExpr(E->getSubExpr()); + Upper = Ptr; + // Simple thin to wide pointer + bool GEPNeedsVoidPtrCast = SrcTy->getPointeeType()->isFunctionType(); + + // Create a zero bounds pointer if the pointee is incomplete or sizeless + // type. + QualType SrcPointeeType = SrcTy->getPointeeType(); + if (!SrcPointeeType->isIncompleteOrSizelessType()) { + llvm::Value *Count = llvm::ConstantInt::get(CGF.IntPtrTy, 1); + // We assume here that the upper bounds have been checked for overflow + // at initialization. + Upper = Builder.CreateInBoundsGEP(CGF.ConvertTypeForMem(SrcPointeeType), + Upper, Count, "upper"); + if (GEPNeedsVoidPtrCast) { + llvm::Type *PtrTy = Ptr->getType(); + Upper = Builder.CreatePointerCast(Upper, PtrTy); + } + } + } else { + assert((DstTy->isBidiIndexable() && SrcTy->isIndexable()) || + (DstTy->isIndexable() && SrcTy->isBidiIndexable())); + + RValue PtrRV = CGF.EmitAnyExpr(E->getSubExpr()); + Address PtrAddr = PtrRV.getAggregateAddress(); + Ptr = CGF.GetWidePointerElement(PtrAddr, WPIndex::Pointer); + Upper = CGF.GetWidePointerElement(PtrAddr, WPIndex::Upper); + + if (SrcTy->isBidiIndexable()) { + // We need to ensure that we don't create an __indexable pointer with + // { .ptr = null, .ub = non-null }, which allows arbitrary access. + // Thus, if the source pointer is null, we cannot just copy the upper + // bound, since it is possible to create __bidi_indexable pointers with + // { .ptr = null, .lb = non-null, .ub = non-null }. + // We always emit a lower bound check, which should work regardless if the + // source pointer is null or not. This will, however, trap for null + // __bidi_indexable pointer with non-null bounds (such pointers can be + // obtained with pointer arithmetic). + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND); + llvm::Value *Lower = CGF.GetWidePointerElement(PtrAddr, WPIndex::Lower); + llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower); + CGF.EmitBoundsSafetyTrapCheck(Check, + BNS_TRAP_BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND); + } + } + + InitPtrField(Ptr); + InitPtrField(Upper); + + // Initialize lower bound if exist + if (DstTy->getPointerAttributes().hasLowerBound()) { + assert(!SrcTy->getPointerAttributes().hasLowerBound()); + InitPtrField(Ptr); + } +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + /// True if the given aggregate type requires special GC API calls. bool AggExprEmitter::TypeRequiresGCollection(QualType T) { // Only record types have members that might require garbage collection. @@ -859,6 +1523,18 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { RValue rvalue = RValue::getAggregate(valueAddr, atomicSlot.isVolatile()); return EmitFinalDestCopy(valueType, rvalue); } + + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BoundsSafetyPointerCast: + return EmitBoundsSafetyPointerConversion(E); + + case CK_NullToPointer: + return EmitNullToBoundPointer(E); + + case CK_ArrayToPointerDecay: + return EmitArrayToBoundPointerDecay(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_AddressSpaceConversion: return Visit(E->getSubExpr()); @@ -890,6 +1566,25 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_NoOp: case CK_UserDefinedConversion: case CK_ConstructorConversion: + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety : With -fbounds-safety a pointer can be an aggregate type and + // when that's the case, the pointee may still have different CVR + // qualifiers, meaning the pointers themselves may have different canonical + // types. + if (E->getType()->isPointerTypeWithBounds()) { + const auto *LPTy = E->getType()->getAs(); + const auto *RPTy = E->getSubExpr()->getType()->getAs(); + (void)LPTy; + (void)RPTy; + assert(LPTy && RPTy && + LPTy->getPointerAttributes() == RPTy->getPointerAttributes() && + CGF.getContext().hasSameUnqualifiedType(RPTy->getPointeeType(), + RPTy->getPointeeType()) && + "Implicit cast types must be compatible"); + Visit(E->getSubExpr()); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ assert(CGF.getContext().hasSameUnqualifiedType(E->getSubExpr()->getType(), E->getType()) && "Implicit cast types must be compatible"); @@ -899,11 +1594,14 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_LValueBitCast: llvm_unreachable("should not be emitting lvalue bitcast as rvalue"); - case CK_Dependent: case CK_BitCast: - case CK_ArrayToPointerDecay: + /* TO_UPSTREAM(BoundsSafety) ON */ + if (E->getType()->isPointerType()) + return EmitWidePointerBitCast(E); + LLVM_FALLTHROUGH; + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_Dependent: case CK_FunctionToPointerDecay: - case CK_NullToPointer: case CK_NullToMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: @@ -1122,16 +1820,150 @@ void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) { void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) { if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) VisitPointerToDataMemberBinaryOperator(E); + /* TO_UPSTREAM(BoundsSafety) ON */ + else if (E->getType()->isPointerType()) + VisitBoundPointerArithmetic(E); + /* TO_UPSTREAM(BoundsSafety) OFF */ else CGF.ErrorUnsupported(E, "aggregate binary expression"); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void AggExprEmitter::VisitCompoundAssignOperator(CompoundAssignOperator *E) { + bool IsSub = false; + if (E->getOpcode() != BO_AddAssign) { + if (E->getOpcode() != BO_SubAssign) + return CGF.ErrorUnsupported(E, "aggregate binary expression"); + IsSub = true; + } + assert(E->getType()->isPointerTypeWithBounds()); + LValue LV = CGF.EmitLValue(E->getLHS()); + EmitCheckedBoundPointerArithmetic(LV, E, E->getLHS(), E->getRHS(), + E->getRHS()->getType(), IsSub); + EmitFinalDestCopy(E->getType(), LV); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void AggExprEmitter::VisitPointerToDataMemberBinaryOperator( const BinaryOperator *E) { LValue LV = CGF.EmitPointerToDataMemberBinaryExpr(E); EmitFinalDestCopy(E->getType(), LV); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void AggExprEmitter::EmitCheckedBoundPointerArithmetic( + LValue DestLV, const Expr *E, const Expr *Base, llvm::Value *Idx, + QualType IdxType, bool IsSub) { + const bool IsSigned = IdxType->isSignedIntegerOrEnumerationType(); + LValue BaseLV = CGF.EmitAggExprToLValue(Base); + EmitBoundPointerArithmetic(DestLV, BaseLV, Idx, IsSigned, IsSub); + + if (CGF.SanOpts.has(SanitizerKind::ArrayBounds)) + CGF.EmitBoundsCheck(E, Base, Idx, IdxType, + /*Accessed*/ false); + + if (CGF.SanOpts.has(SanitizerKind::PointerOverflow)) { + Address PtrAddr = Builder.CreateStructGEP(BaseLV.getAddress(), 0); + llvm::Value *Ptr = Builder.CreateLoad(PtrAddr); + + QualType PointeeType = Base->getType()->getPointeeType(); + CGF.EmitCheckedInBoundsGEP(CGF.ConvertTypeForMem(PointeeType), Ptr, Idx, + IsSigned, IsSub, E->getExprLoc(), "add.ptr"); + } +} + +void AggExprEmitter::EmitCheckedBoundPointerArithmetic( + LValue DestLV, const Expr *E, const Expr *Base, const Expr *Idx, + QualType IdxType, bool IsSub) { + llvm::Value *IdxVal = CGF.EmitScalarExpr(Idx); + EmitCheckedBoundPointerArithmetic(DestLV, E, Base, IdxVal, IdxType, IsSub); +} + +void AggExprEmitter::EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, + llvm::Value *Idx, bool IsSigned, bool IsSub) { + LValue BaseLV = CGF.EmitAggExprToLValue(Base); + EmitBoundPointerArithmetic(DestLV, BaseLV, Idx, IsSigned, IsSub); +} + +void AggExprEmitter::EmitBoundPointerArithmetic(LValue DestLV, LValue BaseLV, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub) { + // TODO(dliew): rdar://109574814: opt-remarks could be added here. + Address PtrAddr = Builder.CreateStructGEP(BaseLV.getAddress(), 0); + llvm::Value *Ptr = Builder.CreateLoad(PtrAddr); + + const PointerType *BasePtrTy = BaseLV.getType()->getAs(); + const PointerType *DestPtrTy = DestLV.getType()->getAs(); + + llvm::Value *OldPtr; + if (BasePtrTy->isIndexable()) + OldPtr = Builder.CreatePtrToInt(Ptr, CGF.SizeTy, "bound.ptr.arith.old"); + + if (auto IntIdxTy = dyn_cast(Idx->getType())) { + if (IntIdxTy->getBitWidth() != CGF.IntPtrTy->getBitWidth()) + Idx = Builder.CreateIntCast(Idx, CGF.IntPtrTy, IsIdxSigned, "idxprom"); + } + + if (IsSub) + Idx = Builder.CreateNeg(Idx, "idx.neg"); + + QualType BaseType = BaseLV.getType(); + assert(BaseType->isPointerType()); + QualType BasePointeeType = BaseType->getPointeeType(); + Ptr = Builder.CreateGEP(CGF.ConvertTypeForMem(BasePointeeType), Ptr, Idx, + "bound.ptr.arith"); + unsigned I = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address DstAddr = Builder.CreateStructGEP(DestLV.getAddress(), I++); + Builder.CreateStore(Val, DstAddr); + }; + + InitPtrField(Ptr); + // Load and store upper bound + Address SrcAddr = Builder.CreateStructGEP(BaseLV.getAddress(), I); + InitPtrField(Builder.CreateLoad(SrcAddr)); + if (DestPtrTy->getPointerAttributes().hasLowerBound()) { + if (BasePtrTy->getPointerAttributes().hasLowerBound()) { + SrcAddr = Builder.CreateStructGEP(BaseLV.getAddress(), I); + InitPtrField(Builder.CreateLoad(SrcAddr)); + } else { + // converting from an indexable pointer, for which the lower bound is the + // same as the original pointer value + InitPtrField(Ptr); + } + } + + if (BasePtrTy->isIndexable()) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_INDEXABLE_PTR_NEW_LT_OLD); + llvm::Value *NewPtr = + Builder.CreatePtrToInt(Ptr, CGF.SizeTy, "bound.ptr.arith.new"); + llvm::Value *NewGEOld = Builder.CreateICmpUGE(NewPtr, OldPtr); + CGF.EmitBoundsSafetyTrapCheck( + NewGEOld, BoundsSafetyTrapKind::BNS_TRAP_INDEXABLE_PTR_NEW_LT_OLD); + } +} + +void AggExprEmitter::VisitBoundPointerArithmetic(const BinaryOperator *E) { + Expr *LHS = E->getLHS(); + Expr *RHS = E->getRHS(); + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + if (E->getOpcode() == BO_Add) { + if (RHS->getType()->isPointerType()) + std::swap(LHS, RHS); + if (!RHS->getType()->isPointerType()) + return EmitCheckedBoundPointerArithmetic(DestLV, E, LHS, RHS, + RHS->getType()); + } else if (E->getOpcode() == BO_Sub) + return EmitCheckedBoundPointerArithmetic(DestLV, E, LHS, RHS, + RHS->getType(), /* IsSub */ true); + + return CGF.ErrorUnsupported(E, "aggregate binary expression"); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Is the value of the given expression possibly a reference to or /// into a __block variable? static bool isBlockVarRef(const Expr *E) { @@ -1234,7 +2066,8 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { return; } - LValue LHS = CGF.EmitLValue(E->getLHS()); + // TO_UPSTREAM(BoundsSafety) + LValue LHS = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); // If we have an atomic type, evaluate into the destination and then // do an atomic copy. @@ -1257,6 +2090,13 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { CGF.EmitAggExpr(E->getRHS(), LHSSlot); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (CGF.SanOpts.has(SanitizerKind::NullabilityAssign) && E->getLHS()->getType()->isPointerTypeWithBounds()) { + llvm::Value* RHSValue = CGF.GetWidePointerElement(LHSSlot.getAddress(), WPIndex::Pointer); + CGF.EmitNullabilityCheck(E->getLHS()->getType(), RHSValue, E->getExprLoc()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Copy into the destination if the assignment isn't ignored. EmitFinalDestCopy(E->getType(), LHS); @@ -1456,6 +2296,10 @@ static bool castPreservesZero(const CastExpr *CE) { case CK_MatrixCast: case CK_NonAtomicToAtomic: case CK_AtomicToNonAtomic: + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: null pointer becomes pointers with null bounds + case CK_BoundsSafetyPointerCast: + /* TO_UPSTREAM(BoundsSafety) OFF */ case CK_HLSLVectorTruncation: return true; diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 8eb6ab7381acb..21b593bb28369 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -454,7 +454,8 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, // Emit the 'this' pointer. Address This = Address::invalid(); if (BO->getOpcode() == BO_PtrMemI) - This = EmitPointerWithAlignment(BaseExpr, nullptr, nullptr, KnownNonNull); + This = EmitPointerWithAlignment(BaseExpr, nullptr, nullptr, nullptr, + nullptr, KnownNonNull); else This = EmitLValue(BaseExpr, KnownNonNull).getAddress(); diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index 4d45f6d64c1cd..db1dbc7ed7eae 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -623,6 +623,8 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, case CK_FixedPointToIntegral: case CK_IntegralToFixedPoint: case CK_MatrixCast: + // TO_UPSTREAM(BoundsSafety) + case CK_BoundsSafetyPointerCast: case CK_HLSLVectorTruncation: case CK_HLSLArrayRValue: llvm_unreachable("invalid cast kind for complex value"); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index e12374957d99e..6ec5ff4675c36 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1027,6 +1027,64 @@ EmitArrayConstant(CodeGenModule &CGM, llvm::ArrayType *DesiredType, return llvm::ConstantStruct::get(SType, Elements); } +/* TO_UPSTREAM(BoundsSafety) ON */ +static llvm::Constant * +EmitWidePointerConstant(CodeGenModule &CGM, llvm::StructType *widePtrStructTy, + ArrayRef Elements) { + const auto numElems = widePtrStructTy->getNumElements(); + assert(numElems == 2 || numElems == 3); + + assert(widePtrStructTy->getElementType(0) == + widePtrStructTy->getElementType(1)); + if (numElems == 3) { + assert(widePtrStructTy->getElementType(0) == + widePtrStructTy->getElementType(2)); + } + + assert(Elements.size() == numElems); + return llvm::ConstantStruct::get(widePtrStructTy, Elements); +} + +static llvm::Constant * +EmitWidePointerConstant(CodeGenModule &CGM, llvm::StructType *widePtrStructTy, + llvm::Constant *Ptr, llvm::Constant *Size, + llvm::Constant *Base = nullptr) { + const auto numElems = widePtrStructTy->getNumElements(); + assert(numElems == 2 || numElems == 3); + + auto widePtrStructElemTy = widePtrStructTy->getElementType(0); + + if (!Ptr->getType()->isPointerTy()) + Ptr = llvm::ConstantExpr::getIntToPtr(Ptr, widePtrStructElemTy); + else if (Ptr->getType() != widePtrStructElemTy) + Ptr = llvm::ConstantExpr::getPointerCast(Ptr, widePtrStructElemTy); + + if (Base) { + if (!Base->getType()->isPointerTy()) + Base = llvm::ConstantExpr::getIntToPtr(Base, widePtrStructElemTy); + else if (Base->getType() != widePtrStructElemTy) + Base = llvm::ConstantExpr::getPointerCast(Base, widePtrStructElemTy); + } else { + Base = Ptr; + } + + llvm::Constant *Upper = llvm::ConstantExpr::getIntToPtr( + llvm::ConstantExpr::getAdd( + llvm::ConstantExpr::getPtrToInt(Base, CGM.IntPtrTy), + llvm::ConstantFoldIntegerCast(Size, CGM.IntPtrTy, /*IsSigned*/ false, + CGM.getDataLayout())), + widePtrStructElemTy); + + llvm::SmallVector Elems; + Elems.push_back(Ptr); + Elems.push_back(Upper); + if (numElems == 3) { + Elems.push_back(Base); + } + return llvm::ConstantStruct::get(widePtrStructTy, Elems); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + // This class only needs to handle arrays, structs and unions. Outside C++11 // mode, we don't currently constant fold those types. All other types are // handled by constant folding. @@ -1166,6 +1224,13 @@ class ConstExprEmitter case CK_ConstructorConversion: return Visit(subExpr, destType); + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: Since we get to emit constant expression we assume + // -fbounds-safety pointer cast is side-effect free. + case CK_BoundsSafetyPointerCast: + return Emitter.tryEmitPrivate(subExpr, destType); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_ArrayToPointerDecay: if (const auto *S = dyn_cast(subExpr)) return CGM.GetAddrOfConstantStringFromLiteral(S).getPointer(); @@ -1425,6 +1490,15 @@ class ConstExprEmitter return Const.build(ValTy, HasFlexibleArray); } + /* TO_UPSTREAM(BoundsSafety) ON */ + // Have the evaluator handle this later as APValue. This is to make it + // consistent with how a normal C cast const-evaluated. See `VisitCastExpr` + // returning nullptr for `CK_BitCast` so the evaluater can handle it later. + llvm::Constant *VisitForgePtrExpr(const ForgePtrExpr *E, QualType Ty) { + return nullptr; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + llvm::Constant *VisitCXXConstructExpr(const CXXConstructExpr *E, QualType Ty) { if (!E->getConstructor()->isTrivial()) @@ -2056,13 +2130,44 @@ class ConstantLValueEmitter : public ConstStmtVisitor(destTy) || isa(destTy)); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto DestPtrTy = DestType->getAs(); + const bool IsWidePtr = DestPtrTy && !DestPtrTy->hasRawPointerLayout(); + assert(isa(destTy) || isa(destTy) || + IsWidePtr); + /*TO_UPSTREAM(BoundsSafety) OFF*/ // If there's no base at all, this is a null or absolute pointer, // possibly cast back to an integer type. if (!base) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (IsWidePtr) { + auto widePtrStructTy = dyn_cast_or_null(destTy); + assert(widePtrStructTy && widePtrStructTy->getNumElements() > 1); + auto widePtrStructElemTy = widePtrStructTy->getElementType(0); + llvm::Constant *Size = nullptr; + if (Value.isLValueForgeBidi()) { + Size = CGM.getSize(Value.getLValueForgedSize()); + } else if (Value.isLValueForgeSingle() && + !Value.getUnwrappedLValueOffset().isZero()) { + // Single points to one valid element. + Size = CGM.getSize( + CGM.getContext().getTypeSizeInChars(DestType->getPointeeType())); + } + + if (Size) { + auto Ptr = llvm::ConstantExpr::getIntToPtr( + CGM.getSize(Value.getUnwrappedLValueOffset()), widePtrStructElemTy); + return EmitWidePointerConstant(CGM, widePtrStructTy, Ptr, Size); + } + + auto Ptr = tryEmitAbsolute(widePtrStructElemTy); + llvm::SmallVector Elems; + for (unsigned i = 0; i < widePtrStructTy->getNumElements(); ++i) + Elems.push_back(Ptr); + return EmitWidePointerConstant(CGM, widePtrStructTy, Elems); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return tryEmitAbsolute(destTy); } @@ -2093,6 +2232,12 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { llvm::Constant *value = result.Value; if (!value) return nullptr; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Value.isLValueForge()) + value = applyForgedOffset(value); + llvm::Constant *BaseAddr = value; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Apply the offset if necessary and not already done. if (!result.HasOffsetApplied) { value = applyOffset(value); @@ -2110,7 +2255,71 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { // an integer. FIXME: performAddrSpaceCast if (isa(destTy)) return llvm::ConstantExpr::getPointerCast(value, destTy); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (IsWidePtr) { + uint64_t UpperBoundBytes = 1; + if (Value.isLValueForgeBidi()) { + UpperBoundBytes = Value.getLValueForgedSize().getQuantity(); + } else if (Value.isLValueForgeSingle()) { + if (DestType->getPointeeType()->isIncompleteOrSizelessType()) { + UpperBoundBytes = 0; + } else { + UpperBoundBytes = CGM.getContext() + .getTypeSizeInChars(DestType->getPointeeType()) + .getQuantity(); + } + } else if (const ValueDecl *InitDecl = base.dyn_cast()) { + // In the following example, `InitDecl` picks up the first declaration, + // `extern float init[]` which is identified as an incomplete array type. + // Thus, the following code block tries to find the most recent + // declaration which will have a concrete array type if that exists. + // + // extern float init[]; + // float init[] = {1, 2, 3, 4}; + // float *__bidi_indexable f[] = {init}; + auto *Var = dyn_cast(InitDecl); + if (Var && Var->hasDefinition() == VarDecl::Definition) { + InitDecl = Var->getDefinition(); + } + QualType InitDeclTy = InitDecl->getType(); + if (auto ValTy = InitDeclTy.getTypePtrOrNull()) { + if (const auto IAT = dyn_cast(ValTy)) { + if (const auto *DCPTy = InitDeclTy->getAs()) { + const Expr *Count = DCPTy->getCountExpr(); + clang::Expr::EvalResult EvaluatedDynamicCount; + const bool DynamicCountEvalSuccess = Count->EvaluateAsInt( + EvaluatedDynamicCount, CGM.getContext(), + clang::Expr::SE_NoSideEffects, /*InConstantContext=*/false); + (void)DynamicCountEvalSuccess; + assert(DynamicCountEvalSuccess); + + const uint64_t SizeOfPointee = + CGM.GetTargetTypeStoreSize( + CGM.getTypes().ConvertType(IAT->getElementType())) + .getQuantity(); + UpperBoundBytes = + EvaluatedDynamicCount.Val.getInt().getLimitedValue() * + SizeOfPointee; + } else { + // Currently, Sema reports this case as a warning. We might want to + // turn certain cases into an error (rdar://84950239). + UpperBoundBytes = 0; + } + } else { + UpperBoundBytes = + CGM.GetTargetTypeStoreSize(CGM.getTypes().ConvertType(InitDeclTy)) + .getQuantity(); + } + } + } + auto widePtrStructTy = dyn_cast_or_null(destTy); + assert(widePtrStructTy); + return EmitWidePointerConstant( + CGM, widePtrStructTy, value, + llvm::ConstantInt::get(CGM.SizeTy, UpperBoundBytes), BaseAddr); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return llvm::ConstantExpr::getPtrToInt(value, destTy); } @@ -2130,8 +2339,9 @@ ConstantLValueEmitter::tryEmitAbsolute(llvm::Type *destTy) { // FIXME: signedness depends on the original integer type. auto intptrTy = CGM.getDataLayout().getIntPtrType(destPtrTy); llvm::Constant *C; - C = llvm::ConstantFoldIntegerCast(getOffset(), intptrTy, /*isSigned*/ false, - CGM.getDataLayout()); + // TO_UPSTREAM(BoundsSafety): getTotalOffset + C = llvm::ConstantFoldIntegerCast(getTotalOffset(), intptrTy, + /*isSigned*/ false, CGM.getDataLayout()); assert(C && "Must have folded, as Offset is a ConstantInt"); C = llvm::ConstantExpr::getIntToPtr(C, destPtrTy); return C; @@ -2140,7 +2350,8 @@ ConstantLValueEmitter::tryEmitAbsolute(llvm::Type *destTy) { ConstantLValue ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { // Handle values. - if (const ValueDecl *D = base.dyn_cast()) { + // TO_UPSTREAM(BoundsSafety): getValueDecl + if (const ValueDecl *D = base.getValueDecl()) { // The constant always points to the canonical declaration. We want to look // at properties of the most recent declaration at the point of emission. D = cast(D->getMostRecentDecl()); @@ -2693,9 +2904,14 @@ llvm::Constant *ConstantEmitter::emitNullForMemory(CodeGenModule &CGM, } llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) { - if (T->getAs()) - return getNullPointer( - cast(getTypes().ConvertTypeForMem(T)), T); + if (const auto *PT = T->getAs()) { + if (PT->hasRawPointerLayout()) + return getNullPointer( + cast(getTypes().ConvertTypeForMem(T)), T); + /*TO_UPSTREAM(BoundsSafety) ON*/ + return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } if (getTypes().isZeroInitializable(T)) return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index a2fb8c8104339..1b5e597b9942e 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -19,6 +19,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "BoundsSafetyTraps.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -366,6 +367,9 @@ class ScalarExprEmitter SourceLocation Loc, ScalarConversionOpts Opts = ScalarConversionOpts()); + // TO_UPSTREAM(BoundsSafety) + Value *EmitBoundsSafetyPointerConversion(CastExpr *E); + /// Convert between either a fixed point and other fixed point or fixed point /// and an integer. Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, @@ -659,6 +663,35 @@ class ScalarExprEmitter return Visit(E->getSubExpr()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + Value *VisitForgePtrExpr(const ForgePtrExpr *E) { + assert((E->ForgesSinglePointer() || E->ForgesTerminatedByPointer()) && + !E->getSize()); + RValue AddrRV = CGF.EmitAnyExpr(E->getAddr()); + llvm::Value *Ptr; + if (AddrRV.isScalar()) { + Ptr = AddrRV.getScalarVal(); + } else { + assert(AddrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(AddrRV.getAggregateAddress(), + WPIndex::Pointer); + } + + llvm::Type *RawPtrTy = CGF.ConvertType(E->getType()); + if (Ptr->getType() != RawPtrTy) { + Ptr = Builder.CreateBitOrPointerCast(Ptr, RawPtrTy); + } + // __builtin_unsafe_forge_* always returns `void *`, regardless of the type + // of the address argument. This is semantically similar to an explicit cast + // to `void *` (i.e., `(void*)addr`), which may require a ptrauth resign to + // match the discriminator for `(void *)` if `addr` was a function pointer + // type or type with a different key and/or discriminator. + // `AuthPointerToPointerCast` skips resign when it's not necessary. + return CGF.authPointerToPointerCast(Ptr, E->getAddr()->getType(), + E->getType()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // C++ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); @@ -926,6 +959,76 @@ class ScalarExprEmitter Value *VisitPackIndexingExpr(PackIndexingExpr *E) { return Visit(E->getSelectedExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + Value *VisitBoundsCheckExpr(BoundsCheckExpr *BCE) { + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : BCE->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(CGF, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + { + RAIIDisableUBSanChecks DisableChecks(CGF); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + CGF.EmitBoundsSafetyTrapCheck(BCE->getCond(), BNS_TRAP_GENERAL); + } + Value *Res = Visit(BCE->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(CGF); + } + return Res; + } + + Value *VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *BCE) { + CGF.EmitFlexibleArrayCountCheck(BCE); + return Visit(BCE->getGuardedExpr()); + } + + Value *VisitAssumptionExpr(AssumptionExpr *AE) { + llvm::Function *FnAssume = CGF.CGM.getIntrinsic(llvm::Intrinsic::assume); + for (Expr *Assumption : AE->assumptions()) { + Builder.CreateCall(FnAssume, CGF.EvaluateExprAsBool(Assumption)); + } + return Visit(AE->getWrappedExpr()); + } + + Value *VisitGetBoundExpr(GetBoundExpr *E) { + RValue RV = CGF.EmitAnyExpr(E->getSubExpr()); + if (RV.isScalar()) + return RV.getScalarVal(); + + Address Addr = RV.getAggregateAddress(); + WPIndex SrcIndex = E->getBoundKind() == GetBoundExpr::BK_Lower + ? WPIndex::Lower : WPIndex::Upper; + return CGF.GetWidePointerElement(Addr, SrcIndex); + } + + Value *VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE) { + if (MSE->isUnbinding()) { + Value *Result = Visit(MSE->getWrappedExpr()); + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(CGF, OVE); + return Result; + } + + for (auto *OVE : MSE->opaquevalues()) { + if (OVE->getType()->isPointerTypeWithBounds()) { + RValue PtrRV = CGF.EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = CGF.MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, OVE->getSourceExpr()); + } + } + return Visit(MSE->getWrappedExpr()); + } + + Value *VisitTerminatedByFromIndexableExpr(TerminatedByFromIndexableExpr *E); + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace. @@ -1258,6 +1361,27 @@ void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType, {Src, Dst}); } +/* TO_UPSTREAM(BoundsSafety) ON */ +Value *ScalarExprEmitter::EmitBoundsSafetyPointerConversion(CastExpr *E) { + QualType DstTy = E->getType(); + QualType SrcTy = E->getSubExpr()->getType(); + assert(!DstTy->isPointerTypeWithBounds()); + + if (SrcTy->isPointerTypeWithBounds()) { + bool NeedBoundsCheck = + DstTy->isSinglePointerType() && !DstTy->isBoundsAttributedType(); + bool TrackSize = false; + return CGF.EmitWideToRawPtr( + E->getSubExpr(), /*BoundsCheck=*/NeedBoundsCheck, + /*TrapCtx=*/BoundsSafetyTrapCtx::CAST, /*LowerOnlyCheck=*/TrackSize, + /*UpperPtr=*/nullptr, /*LowerPtr=*/nullptr, + /*IsNullAllowed=*/DstTy->isSinglePointerType()); + } + // No additional check is needed between single and unsafe pointers. + return Visit(E->getSubExpr()); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + // Should be called within CodeGenFunction::SanitizerScope RAII scope. // Returns 'i1 false' when the truncation Src -> Dst was lossy. static std::pair(E)); } + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: + return EmitBoundsSafetyPointerConversion(CE); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_BaseToDerived: { const CXXRecordDecl *DerivedClassDecl = DestTy->getPointeeCXXRecordDecl(); assert(DerivedClassDecl && "BaseToDerived arg isn't a C++ object pointer!"); @@ -2584,7 +2713,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_PointerToIntegral: { assert(!DestTy->isBooleanType() && "bool should use PointerToBool"); - auto *PtrExpr = Visit(E); + /*TO_UPSTREAM(BoundsSafety) ON*/ + Value *PtrExpr = E->getType()->isPointerTypeWithBounds() ? + CGF.EmitWideToRawPtr(E) : Visit(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ if (CGF.CGM.getCodeGenOpts().StrictVTablePointers) { const QualType SrcType = E->getType(); @@ -2708,8 +2840,19 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_IntegralToBoolean: return EmitIntToBoolConversion(Visit(E)); - case CK_PointerToBoolean: - return EmitPointerToBoolConversion(Visit(E), E->getType()); + case CK_PointerToBoolean: { + /*TO_UPSTREAM(BoundsSafety) ON*/ + Value *PtrExpr; + QualType PtrTy = E->getType(); + if (E->getType()->isPointerTypeWithBounds()) { + PtrExpr = CGF.EmitWideToRawPtr(E); + PtrTy = CGF.getContext().getPointerType(PtrTy->getPointeeType()). + withCVRQualifiers(PtrTy.getCVRQualifiers()); + } else + PtrExpr = Visit(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return EmitPointerToBoolConversion(PtrExpr, PtrTy); + } case CK_FloatingToBoolean: { CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE); return EmitFloatToBoolConversion(Visit(E)); @@ -3003,6 +3146,14 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } else if (const PointerType *ptr = type->getAs()) { QualType type = ptr->getPointeeType(); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (E->getSubExpr()->getType()->isValueTerminatedType()) { + assert(isInc); + CGF.EmitValueTerminatedPointerArithmeticCheck(E->getSubExpr()->getType(), + value); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // VLA types don't have constant size. if (const VariableArrayType *vla = CGF.getContext().getAsVariableArrayType(type)) { @@ -3594,6 +3745,11 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( // Load/convert the LHS. LValue LHSLV = EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LHSLV); + /* TO_UPSTREAM(BoundsSafety) OFF */ + llvm::PHINode *atomicPHI = nullptr; if (const AtomicType *atomicTy = LHSTy->getAs()) { QualType type = atomicTy->getValueType(); @@ -3968,6 +4124,20 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, std::swap(pointerOperand, indexOperand); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (pointerOperand->getType()->isValueTerminatedType()) { + // The index should be an ICE, since it was checked in Sema. + llvm::APSInt IndexVal = + indexOperand->getIntegerConstantExpr(CGF.getContext()).value(); + if (!IndexVal.isZero()) { + assert((!isSubtraction && IndexVal.isOne()) || + (isSubtraction && IndexVal.isNegative() && IndexVal.isAllOnes())); + CGF.EmitValueTerminatedPointerArithmeticCheck(pointerOperand->getType(), + pointer); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isSigned = indexOperand->getType()->isSignedIntegerOrEnumerationType(); unsigned width = cast(index->getType())->getBitWidth(); @@ -4860,6 +5030,12 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { llvm::Value *RV = CGF.EmitPointerAuthQualify(ptrauth, E->getRHS(), LV.getAddress()); CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc()); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LV); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + CGF.EmitStoreThroughLValue(RValue::get(RV), LV); if (Ignore) return nullptr; @@ -4902,6 +5078,11 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { LHS = EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LHS); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Store the value into the LHS. Bit-fields are handled specially // because the result is altered by the store, i.e., [C99 6.5.16p1] // 'An assignment expression has the value of the left operand after @@ -5911,3 +6092,164 @@ Address CodeGenFunction::EmitCheckedInBoundsGEP( IdxList, SignedIndices, IsSubtraction, Loc, Name), elementType, Align); } + +/* TO_UPSTREAM(BoundsSafety) ON */ +static llvm::Value *EmitTerminatedByFromIndexable(CodeGenFunction &CGF, + const Expr *PtrExpr, + const llvm::APSInt &TermVal) { + auto &Builder = CGF.Builder; + + llvm::Value *Upper = nullptr; + Address PtrAddr = CGF.EmitPointerWithAlignment(PtrExpr, /*BaseInfo=*/nullptr, + /*TBAAInfo=*/nullptr, &Upper); + assert(Upper); + llvm::Value *Ptr = PtrAddr.getBasePointer(); + llvm::Type *ElemTy = PtrAddr.getElementType(); + + // Emit a loop to find the terminator. + + llvm::BasicBlock *Cond = CGF.createBasicBlock("terminated_by.loop_cond"); + llvm::BasicBlock *Cont = CGF.createBasicBlock("terminated_by.loop_cont"); + llvm::BasicBlock *End = CGF.createBasicBlock("terminated_by.loop_end"); + + // Ensure that Ptr < Upper. + CGF.EmitBoundsSafetyBoundsCheck(ElemTy, Ptr, Upper, /*Lower=*/nullptr, + /*AcceptPtrAndUpperNull=*/false, + BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE); + + RawAddress CurAllocaAddr = + CGF.CreateDefaultAlignTempAlloca(Ptr->getType(), "terminated_by.cur"); + Builder.CreateStore(Ptr, CurAllocaAddr); + + CGF.EmitBlock(Cond); + + // Ensure that Cur+1 <= Upper. The initial value of Cur is Ptr, and thus + // smaller than Upper. We assume that Cur+1 won't overflow (this can only + // happen if (void*)Upper+sizeof(ElemTy) overflows). + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + llvm::Value *Cur = nullptr; + llvm::Value *OnePastCur = nullptr; + llvm::Value *AccessCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM); + Cur = Builder.CreateLoad(CurAllocaAddr, "terminated_by.cur"); + + OnePastCur = Builder.CreateInBoundsGEP(ElemTy, Cur, One, + "terminated_by.one_past_cur"); + AccessCheck = + Builder.CreateICmpULE(OnePastCur, Upper, "terminated_by.check_access"); + CGF.EmitBoundsSafetyTrapCheck(AccessCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM); + } + + // Check terminator. + Address ElemAddr = Address(Cur, ElemTy, PtrAddr.getAlignment()); + llvm::Value *Elem = Builder.CreateLoad(ElemAddr); + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + llvm::Value *TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminated_by.check_terminator"); + Builder.CreateCondBr(TermCheck, End, Cont); + + CGF.EmitBlock(Cont); + + Builder.CreateStore(OnePastCur, CurAllocaAddr); + Builder.CreateBr(Cond); + + CGF.EmitBlock(End); + + return Ptr; +} + +static llvm::Value *EmitTerminatedByFromIndexableWithPtrToTerm( + CodeGenFunction &CGF, const Expr *PtrExpr, const Expr *PtrToTermExpr, + const llvm::APSInt &TermVal) { + auto &Builder = CGF.Builder; + + llvm::Value *Upper = nullptr; + Address PtrAddr = CGF.EmitPointerWithAlignment(PtrExpr, /*BaseInfo=*/nullptr, + /*TBAAInfo=*/nullptr, &Upper); + assert(Upper); + llvm::Value *Ptr = PtrAddr.getBasePointer(); + llvm::Type *ElemTy = PtrAddr.getElementType(); + + Address PtrToTermAddr = CGF.EmitPointerWithAlignment(PtrToTermExpr); + llvm::Value *PtrToTerm = PtrToTermAddr.getBasePointer(); + + // Ptr <= PtrToTerm + llvm::Value *PtrCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR); + + PtrCheck = Builder.CreateICmpULE(Ptr, PtrToTerm, + "terminated_by.check_ptr_le_term_ptr"); + CGF.EmitBoundsSafetyTrapCheck( + PtrCheck, BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR); + } + + // PtrToTerm+sizeof(ElemTy) does not overflow + llvm::Value *DidNotOverflow = nullptr; + llvm::Value *OnePastPtrToTerm = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW); + + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + OnePastPtrToTerm = Builder.CreateGEP(ElemTy, PtrToTerm, One, + "terminated_by.one_past_term_ptr"); + + DidNotOverflow = + Builder.CreateICmpUGT(OnePastPtrToTerm, PtrToTerm, + "terminated_by.check_one_past_term_ptr_overflow"); + CGF.EmitBoundsSafetyTrapCheck( + DidNotOverflow, BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW); + } + + // PtrToTerm+sizeof(ElemTy) <= Upper + llvm::Value *OnePastPtrToTermCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, + BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND); + + OnePastPtrToTermCheck = + Builder.CreateICmpULE(OnePastPtrToTerm, Upper, + "terminated_by.check_one_past_term_ptr_le_upper"); + CGF.EmitBoundsSafetyTrapCheck( + OnePastPtrToTermCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND); + } + + // Check terminator. + llvm::Value *TermCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM); + + llvm::Value *Elem = Builder.CreateLoad(PtrToTermAddr); + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminated_by.check_terminator"); + CGF.EmitBoundsSafetyTrapCheck(TermCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM); + } + + return Ptr; +} + +Value *ScalarExprEmitter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + // The pointer should have been cast to an __indexable pointer in Sema. + assert(E->getPointer()->getType()->isIndexablePointerType()); + + const auto *VTT = E->getType()->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(CGF.getContext()); + + if (const auto *PtrToTermExpr = E->getPointerToTerminator()) { + return EmitTerminatedByFromIndexableWithPtrToTerm(CGF, E->getPointer(), + PtrToTermExpr, TermVal); + } + return EmitTerminatedByFromIndexable(CGF, E->getPointer(), TermVal); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ \ No newline at end of file diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 084c8409c69b3..aba8de5c6e44c 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -103,6 +103,8 @@ add_clang_library(clangCodeGen CodeGenPGO.cpp CodeGenTBAA.cpp CodeGenTypes.cpp + BoundsSafetyOptRemarks.cpp + BoundsSafetyTraps.cpp ConstantInitBuilder.cpp CoverageMappingGen.cpp ItaniumCXXABI.cpp diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 8ce213ac38dc2..b911a6b9567e3 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -40,6 +40,8 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/FPEnv.h" +// TO_UPSTREAM(BoundsSafety) ON +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" @@ -266,9 +268,17 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { case Type::DeducedTemplateSpecialization: llvm_unreachable("undeduced type in IR-generation"); + /* TO_UPSTREAM(BoundsSafety) ON*/ + case Type::Pointer: { + auto const *PT = dyn_cast(type); + if (!PT->hasRawPointerLayout()) + return TEK_Aggregate; + return TEK_Scalar; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Various scalar types. case Type::Builtin: - case Type::Pointer: case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: @@ -307,6 +317,43 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { } } +/* TO_UPSTREAM(BoundsSafety) ON*/ +llvm::BasicBlock *CodeGenFunction::createUnmergeableBasicBlock( + const Twine &name, llvm::Function *parent, llvm::BasicBlock *before) { + auto *BB = createBasicBlock(name, parent, before); + // This approach is the same approach used by Swift. + // TODO: Find a better way to do this (rdar://137627723). + + // Emit unique side-effecting inline asm calls in order to eliminate + // the possibility that an LLVM optimization or code generation pass + // will merge these blocks back together again. We emit an empty asm + // string with the side-effect flag set, and with a unique integer + // argument. + llvm::IntegerType *asmArgTy = CGM.Int64Ty; + llvm::Type *argTys = {asmArgTy}; + llvm::FunctionType *asmFnTy = + llvm::FunctionType::get(CGM.VoidTy, argTys, /* isVarArg=*/false); + // "n" is an input constraint stating that the first argument to the call + // will be an integer literal. + llvm::InlineAsm *inlineAsm = + llvm::InlineAsm::get(asmFnTy, /*AsmString=*/"", /*Constraints=*/"n", + /*hasSideEffects=*/true); + + // Use the builder so that any opt-remarks and attributes are automatically + // applied by the builder. The current state of the builder is saved so it + // can be used for creating the asm call and then the builder is reset to + // its previous state. + auto OldInsertPoint = Builder.GetInsertPoint(); + auto* OldInsertBB = Builder.GetInsertBlock(); + Builder.SetInsertPoint(BB); + Builder.CreateCall( + inlineAsm, + llvm::ConstantInt::get(asmArgTy, CGM.getAndIncrementUniqueTrapCount())); + Builder.SetInsertPoint(OldInsertBB, OldInsertPoint); + return BB; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::DebugLoc CodeGenFunction::EmitReturnBlock() { // For cleanliness, we try to avoid emitting the return block for // simple cases. @@ -2480,6 +2527,10 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ // Keep walking after single level desugaring. type = type.getSingleStepDesugaredType(getContext()); break; @@ -2666,12 +2717,50 @@ CodeGenFunction::SanitizerScope::~SanitizerScope() { CGF->IsSanitizerScope = false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +CodeGenFunction::BoundsSafetyOptRemarkScope::BoundsSafetyOptRemarkScope( + CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind) + : CGF(CGF), Kind(Kind) { + // Check for dupes + if (InScope(CGF, Kind)) { + assert(0 && "Kind already in scope"); + return; + } + CGF->BoundsSafetyOptRemarkScopes.push_back(this); +} + +CodeGenFunction::BoundsSafetyOptRemarkScope::~BoundsSafetyOptRemarkScope() { + BoundsSafetyOptRemarkScope *Current = + CGF->BoundsSafetyOptRemarkScopes.pop_back_val(); + assert(Current == this); +} + +void CodeGenFunction::BoundsSafetyOptRemarkScope::Annotate(llvm::Instruction *I) { + I->addAnnotationMetadata(GetBoundsSafetyOptRemarkString(Kind)); +} + +bool CodeGenFunction::BoundsSafetyOptRemarkScope::InScope( + const CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind) { + for (const auto &Scope : CGF->BoundsSafetyOptRemarkScopes) { + if (Scope->GetKind() == Kind) + return true; + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void CodeGenFunction::InsertHelper(llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock::iterator InsertPt) const { LoopStack.InsertHelper(I); if (IsSanitizerScope) I->setNoSanitizeMetadata(); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + for (const auto &Scope : BoundsSafetyOptRemarkScopes) { + Scope->Annotate(I); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } void CGBuilderInserter::InsertHelper( diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 2e7a4e60defee..513e4b320ec9d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -20,6 +20,8 @@ #include "CodeGenModule.h" #include "CodeGenPGO.h" #include "EHScopeStack.h" +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" #include "VarBypassDetector.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" @@ -112,6 +114,8 @@ enum TypeEvaluationKind { TEK_Aggregate }; +/// TODO: Add -fbounds-safety trap kinds + #define LIST_SANITIZER_CHECKS \ SANITIZER_CHECK(AddOverflow, add_overflow, 0) \ SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0) \ @@ -146,6 +150,14 @@ enum SanitizerHandler { #undef SANITIZER_CHECK }; +/* TO_UPSTREAM(BoundsSafety) ON */ +enum class WPIndex { + Pointer = 0, + Upper = 1, + Lower = 2, +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Helper class with most of the code for saving a value for a /// conditional expression cleanup. struct DominatingLLVMValue { @@ -582,6 +594,28 @@ class CodeGenFunction : public CodeGenTypeCache { ~SanitizerScope(); }; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// RAII object that automatically adds BoundsSafety opt-remarks to instructions + /// created with `Builder` while in scope. Multiple instances of this class + /// are allowed to be in scope, but only if they each use distinct + /// opt-remarks. + class BoundsSafetyOptRemarkScope { + CodeGenFunction *CGF; + BoundsSafetyOptRemarkKind Kind; + public: + BoundsSafetyOptRemarkScope(CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind); + BoundsSafetyOptRemarkScope(CodeGenFunction &CGF, BoundsSafetyOptRemarkKind Kind) + : BoundsSafetyOptRemarkScope(&CGF, Kind){}; + ~BoundsSafetyOptRemarkScope(); + void Annotate(llvm::Instruction *I); + BoundsSafetyOptRemarkKind GetKind() const { return Kind; } + static bool InScope(const CodeGenFunction *CGF, + BoundsSafetyOptRemarkKind Kind); + }; + + llvm::SmallVector BoundsSafetyOptRemarkScopes; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// In C++, whether we are code generating a thunk. This controls whether we /// should emit cleanups. bool CurFuncIsThunk = false; @@ -1408,6 +1442,17 @@ class CodeGenFunction : public CodeGenTypeCache { return data; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // FIXME: This can't do 'unprotectFromPeepholes'. We need another + // mechanism to handle OpaqueValueExpr inserted by -fbounds-safety. + static void unbind(CodeGenFunction &CGF, const OpaqueValueExpr *ov) { + if (shouldBindAsLValue(ov)) + CGF.OpaqueLValues.erase(ov); + else + CGF.OpaqueRValues.erase(ov); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isValid() const { return OpaqueValue != nullptr; } void clear() { OpaqueValue = nullptr; } @@ -2607,6 +2652,13 @@ class CodeGenFunction : public CodeGenTypeCache { return llvm::BasicBlock::Create(getLLVMContext(), name, parent, before); } + /* TO_UPSTREM(BoundsSafety) ON*/ + llvm::BasicBlock * + createUnmergeableBasicBlock(const Twine &name = "", + llvm::Function *parent = nullptr, + llvm::BasicBlock *before = nullptr); + /* TO_UPSTREM(BoundsSafety) OFF*/ + /// getBasicBlockForLabel - Return the LLVM basicblock that the specified /// label maps to. JumpDest getJumpDestForLabel(const LabelDecl *S); @@ -2872,6 +2924,36 @@ class CodeGenFunction : public CodeGenTypeCache { AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap); } + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::Value *GetWidePointerElement(Address Addr, WPIndex Index); + /// Emit a raw pointer extraction from wide pointer. + llvm::Value *EmitWideToRawPtr( + const Expr *E, bool BoundsCheck = false, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN, + bool LowerOnlyCheck = false, llvm::Value **UpperPtr = nullptr, + llvm::Value **LowerPtr = nullptr, bool IsNullAllowed = false); + + void EmitPtrCastLECheck( + llvm::Value *LHS, llvm::Value *RHS, BoundsSafetyTrapKind TrapKind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + void EmitBoundsSafetyBoundsCheck( + llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower, bool AcceptNullPt = false, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + void EmitBoundsSafetyRangeCheck( + llvm::Value *LowerBound, llvm::Value *LowerAccess, + llvm::Value *UpperAccess, llvm::Value *UpperBound, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + LValue EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E, + bool Accessed = true); + + llvm::Value *EmitTerminator(const llvm::APSInt &Terminator, llvm::Type *Ty); + void EmitValueTerminatedPointerArithmeticCheck(QualType PointerType, + llvm::Value *Ptr); + void EmitValueTerminatedAssignmentCheck(const BinaryOperator *E, + LValue Value); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// EvaluateExprAsBool - Perform the usual unary conversions on the specified /// expression and compare the result against zero, returning an Int1Ty value. llvm::Value *EvaluateExprAsBool(const Expr *E); @@ -3311,6 +3393,21 @@ class CodeGenFunction : public CodeGenTypeCache { const FieldDecl *FAMDecl, const FieldDecl *CountDecl); + /*TO_UPSTREAM(BoundsSafety) ON*/ + /// Emit a check for the boolean expression \p E. The emitted code checks + /// if \p E is false. If this is the case the emitted code will call the trap + /// intrisic for -fbounds-safety. + /// + /// A BoundsSafetyOptRemarkScope using the opt-remark that corresponds to \p kind + /// must be in scope when this method is called. + void EmitBoundsSafetyTrapCheck(const Expr *E, BoundsSafetyTrapKind kind); + + void EmitFlexibleArrayCountCheck(const PredefinedBoundsCheckExpr *E); + + /// Emit a trap if any of \p Conds doesn't hold \c true. + void EmitSequentialTrapCheck(llvm::ArrayRef Conds); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre); ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, @@ -4294,7 +4391,16 @@ class CodeGenFunction : public CodeGenTypeCache { Address EmitArrayToPointerDecay(const Expr *Array, LValueBaseInfo *BaseInfo = nullptr, - TBAAAccessInfo *TBAAInfo = nullptr); + TBAAAccessInfo *TBAAInfo = nullptr, + // TO_UPSTREAM(BoundsSafety) + TBAAAccessInfo *EltTBAAInfo = nullptr); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + Address EmitArrayToWidePointerDecay(const Expr *Array, llvm::Value *&Upper, + LValueBaseInfo *BaseInfo = nullptr, + TBAAAccessInfo *TBAAInfo = nullptr, + TBAAAccessInfo *EltTBAAInfo = nullptr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ class ConstantEmission { llvm::PointerIntPair ValueAndIsReference; @@ -4358,6 +4464,17 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitCXXTypeidLValue(const CXXTypeidExpr *E); LValue EmitCXXUuidofLValue(const CXXUuidofExpr *E); + /*TO_UPSTREAM(BoundsSafety) ON*/ + LValue EmitBoundsCheckExprLValue(const BoundsCheckExpr *E); + LValue + EmitPredefinedBoundsCheckExprLValue(const PredefinedBoundsCheckExpr *E); + LValue EmitMaterializeSequenceExprLValue(const MaterializeSequenceExpr *E); + LValue EmitBoundsSafetyPointerPromotionExprLValue( + const BoundsSafetyPointerPromotionExpr *E); + LValue EmitForgePtrExprLValue(const ForgePtrExpr *E); + LValue EmitAssumptionExprLValue(const AssumptionExpr *E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + LValue EmitObjCMessageExprLValue(const ObjCMessageExpr *E); LValue EmitObjCIvarRefLValue(const ObjCIvarRefExpr *E); LValue EmitStmtExprLValue(const StmtExpr *E); @@ -5057,6 +5174,12 @@ class CodeGenFunction : public CodeGenTypeCache { /// nonnull, if \p LHS is marked _Nonnull. void EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is + /// nonnull, if \p LHS is marked _Nonnull. + void EmitNullabilityCheck(QualType LHSQTy, llvm::Value *RHS, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// An enumeration which makes it easier to specify whether or not an /// operation is a subtraction. enum { NotSubtraction = false, IsSubtraction = true }; @@ -5123,7 +5246,19 @@ class CodeGenFunction : public CodeGenTypeCache { /// Create a basic block that will call the trap intrinsic, and emit a /// conditional branch to it, for the -ftrapv checks. - void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID); + void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, + StringRef Annotation = "", StringRef TrapMessage = ""); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Create a basic block that will call the trap intrinsic for -fbounds-safety, and + /// emit a conditional branch to it. + /// + /// A BoundsSafetyOptRemarkScope using the opt-remark that corresponds to \p kind + /// must be in scope when this method is called. + void EmitBoundsSafetyTrapCheck( + llvm::Value *Checked, BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + /* TO_UPSTREAM(BoundsSafety) OFF*/ /// Emit a call to trap or debugtrap and attach function attribute /// "trap-func-name" if specified. @@ -5300,9 +5435,17 @@ class CodeGenFunction : public CodeGenTypeCache { /// into the address of a local variable. In such a case, it's quite /// reasonable to just ignore the returned alignment when it isn't from an /// explicit source. + /// + /// TO_UPSTREAM(BoundsSafety) + /// BoundsSafety: Extract bounds information when loading a pointer from + /// wide pointer. Address EmitPointerWithAlignment(const Expr *Addr, LValueBaseInfo *BaseInfo = nullptr, TBAAAccessInfo *TBAAInfo = nullptr, + /* TO_UPSTREAM(BoundsSafety) ON*/ + llvm::Value **UpperPtr = nullptr, + llvm::Value **LowerPtr = nullptr, + /* TO_UPSTREAM(BoundsSafety) OFF*/ KnownNonNull_t IsKnownNonNull = NotKnownNonNull); /// If \p E references a parameter with pass_object_size info or a constant @@ -5364,6 +5507,23 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitAArch64CpuSupports(ArrayRef FeatureStrs); }; +/* TO_UPSTREAM(BoundsSafety) ON*/ +class RAIIDisableUBSanChecks { +public: + explicit RAIIDisableUBSanChecks(CodeGenFunction &CGF) + : CGF(CGF), OldSanOpts(CGF.SanOpts) { + CGF.SanOpts.clear(SanitizerKind::Undefined | + SanitizerKind::Integer | + SanitizerKind::ImplicitConversion); + } + ~RAIIDisableUBSanChecks() { CGF.SanOpts = OldSanOpts; } + +private: + CodeGenFunction &CGF; + SanitizerSet OldSanOpts; +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + inline DominatingLLVMValue::saved_type DominatingLLVMValue::save(CodeGenFunction &CGF, llvm::Value *value) { if (!needsSaving(value)) return saved_type(value, false); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 5b5fd6f9a2019..11e4517acb41c 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -632,6 +632,9 @@ class CodeGenModule : public CodeGenTypeCache { std::optional computeVTPointerAuthentication(const CXXRecordDecl *ThisClass); + // TO_UPSTREAM(BoundsSafety) + uint64_t UniqueTrapCount = 0; + public: CodeGenModule(ASTContext &C, IntrusiveRefCntPtr FS, const HeaderSearchOptions &headersearchopts, @@ -1095,6 +1098,11 @@ class CodeGenModule : public CodeGenTypeCache { /// Fetches the global unique block count. int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Fetches and increments unique trap count + uint64_t getAndIncrementUniqueTrapCount() { return UniqueTrapCount++; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Fetches the type of a generic block descriptor. llvm::Type *getBlockDescriptorType(); diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index 2ce558d4bdf35..aa522f88ff550 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -39,8 +39,9 @@ CodeGenTBAA::CodeGenTBAA(ASTContext &Ctx, CodeGenTypes &CGTypes, llvm::Module &M, const CodeGenOptions &CGO, const LangOptions &Features) : Context(Ctx), CGTypes(CGTypes), Module(M), CodeGenOpts(CGO), - Features(Features), MDHelper(M.getContext()), Root(nullptr), - Char(nullptr) {} + Features(Features), + MangleCtx(ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())), + MDHelper(M.getContext()), Root(nullptr), Char(nullptr) {} CodeGenTBAA::~CodeGenTBAA() { } @@ -202,40 +203,87 @@ llvm::MDNode *CodeGenTBAA::getTypeInfoHelper(const Type *Ty) { // Other qualifiers could theoretically be distinguished, especially if // they involve a significant representation difference. We don't // currently do so, however. - // - // Computing the pointee type string recursively is implicitly more - // forgiving than the standards require. Effectively, we are turning - // the question "are these types compatible/similar" into "are - // accesses to these types allowed to alias". In both C and C++, - // the latter question has special carve-outs for signedness - // mismatches that only apply at the top level. As a result, we are - // allowing e.g. `int *` l-values to access `unsigned *` objects. if (Ty->isPointerType() || Ty->isReferenceType()) { llvm::MDNode *AnyPtr = createScalarTypeNode("any pointer", getChar(), Size); if (!CodeGenOpts.PointerTBAA) return AnyPtr; - // Compute the depth of the pointer and generate a tag of the form "p - // ". + // C++ [basic.lval]p11 permits objects to accessed through an l-value of + // similar type. Two types are similar under C++ [conv.qual]p2 if the + // decomposition of the types into pointers, member pointers, and arrays has + // the same structure when ignoring cv-qualifiers at each level of the + // decomposition. Meanwhile, C makes T(*)[] and T(*)[N] compatible, which + // would really complicate any attempt to distinguish pointers to arrays by + // their bounds. It's simpler, and much easier to explain to users, to + // simply treat all pointers to arrays as pointers to their element type for + // aliasing purposes. So when creating a TBAA tag for a pointer type, we + // recursively ignore both qualifiers and array types when decomposing the + // pointee type. The only meaningful remaining structure is the number of + // pointer types we encountered along the way, so we just produce the tag + // "p ". If we do find a member pointer type, for now + // we just conservatively bail out with AnyPtr (below) rather than trying to + // create a tag that honors the similar-type rules while still + // distinguishing different kinds of member pointer. unsigned PtrDepth = 0; do { PtrDepth++; - Ty = Ty->getPointeeType().getTypePtr(); + Ty = Ty->getPointeeType()->getBaseElementTypeUnsafe(); } while (Ty->isPointerType()); - // TODO: Implement C++'s type "similarity" and consider dis-"similar" - // pointers distinct for non-builtin types. + + // While there are no special rules in the standards regarding void pointers + // and strict aliasing, emitting distinct tags for void pointers break some + // common idioms and there is no good alternative to re-write the code + // without strict-aliasing violations. + if (Ty->isVoidType()) + return AnyPtr; + + assert(!isa(Ty)); + // When the underlying type is a builtin type, we compute the pointee type + // string recursively, which is implicitly more forgiving than the standards + // require. Effectively, we are turning the question "are these types + // compatible/similar" into "are accesses to these types allowed to alias". + // In both C and C++, the latter question has special carve-outs for + // signedness mismatches that only apply at the top level. As a result, we + // are allowing e.g. `int *` l-values to access `unsigned *` objects. + SmallString<256> TyName; if (isa(Ty)) { llvm::MDNode *ScalarMD = getTypeInfoHelper(Ty); StringRef Name = cast( ScalarMD->getOperand(CodeGenOpts.NewStructPathTBAA ? 2 : 0)) ->getString(); - SmallString<256> OutName("p"); - OutName += std::to_string(PtrDepth); - OutName += " "; - OutName += Name; - return createScalarTypeNode(OutName, AnyPtr, Size); + TyName = Name; + } else { + // Be conservative if the type isn't a RecordType. We are specifically + // required to do this for member pointers until we implement the + // similar-types rule. + const auto *RT = Ty->getAs(); + if (!RT) + return AnyPtr; + + // For unnamed structs or unions C's compatible types rule applies. Two + // compatible types in different compilation units can have different + // mangled names, meaning the metadata emitted below would incorrectly + // mark them as no-alias. Use AnyPtr for such types in both C and C++, as + // C and C++ types may be visible when doing LTO. + // + // Note that using AnyPtr is overly conservative. We could summarize the + // members of the type, as per the C compatibility rule in the future. + // This also covers anonymous structs and unions, which have a different + // compatibility rule, but it doesn't matter because you can never have a + // pointer to an anonymous struct or union. + if (!RT->getDecl()->getDeclName()) + return AnyPtr; + + // For non-builtin types use the mangled name of the canonical type. + llvm::raw_svector_ostream TyOut(TyName); + MangleCtx->mangleCanonicalTypeName(QualType(Ty, 0), TyOut); } - return AnyPtr; + + SmallString<256> OutName("p"); + OutName += std::to_string(PtrDepth); + OutName += " "; + OutName += TyName; + return createScalarTypeNode(OutName, AnyPtr, Size); } // Accesses to arrays are accesses to objects of their element types. diff --git a/clang/lib/CodeGen/CodeGenTBAA.h b/clang/lib/CodeGen/CodeGenTBAA.h index ba74a39a4d25e..ab3b05df7713b 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.h +++ b/clang/lib/CodeGen/CodeGenTBAA.h @@ -24,6 +24,7 @@ namespace clang { class ASTContext; class CodeGenOptions; class LangOptions; + class MangleContext; class QualType; class Type; @@ -119,6 +120,7 @@ class CodeGenTBAA { llvm::Module &Module; const CodeGenOptions &CodeGenOpts; const LangOptions &Features; + std::unique_ptr MangleCtx; // MDHelper - Helper for creating metadata. llvm::MDBuilder MDHelper; diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index f5deccdc1ba75..fd4b4e3bb8cec 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -623,7 +623,12 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { const PointerType *PTy = cast(Ty); QualType ETy = PTy->getPointeeType(); unsigned AS = getTargetAddressSpace(ETy); - ResultType = llvm::PointerType::get(getLLVMContext(), AS); + if (PTy->hasRawPointerLayout()) + ResultType = llvm::PointerType::get(getLLVMContext(), AS); + /* TO_UPSTREAM(BoundsSafety) ON */ + else + ResultType = ConvertBoundsSafetyPointerType(PTy); + /* TO_UPSTREAM(BoundsSafety) OFF */ break; } @@ -788,6 +793,35 @@ bool CodeGenModule::isPaddedAtomicType(const AtomicType *type) { return Context.getTypeSize(type) != Context.getTypeSize(type->getValueType()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +llvm::Type *CodeGenTypes::ConvertBoundsSafetyPointerType(const PointerType *PT) { + assert(!PT->hasRawPointerLayout()); + llvm::StructType *&Entry = WidePointerTypes[PT]; + if (Entry) + return Entry; + + SmallString<256> TypeName; + llvm::raw_svector_ostream OS(TypeName); + OS << "__bounds_safety::wide_ptr."; + if (PT->isBidiIndexable()) + OS << "bidi_indexable"; + else if (PT->isIndexable()) + OS << "indexable"; + + QualType RawQTy = Context.getPointerType(PT->getPointeeType()); + llvm::Type *RawTy = ConvertType(RawQTy); + llvm::SmallVector Elems; + Elems.push_back(RawTy); + if (PT->getPointerAttributes().hasUpperBound()) + Elems.push_back(RawTy); + if (PT->getPointerAttributes().hasLowerBound()) + Elems.push_back(RawTy); + + Entry = llvm::StructType::create(getLLVMContext(), Elems, OS.str()); + return Entry; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// ConvertRecordDeclType - Lay out a tagged decl type like struct or union. llvm::StructType *CodeGenTypes::ConvertRecordDeclType(const RecordDecl *RD) { // TagDecl's are not necessarily unique, instead use the (clang) diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h index 5aebf9a212237..be9a25e2ff119 100644 --- a/clang/lib/CodeGen/CodeGenTypes.h +++ b/clang/lib/CodeGen/CodeGenTypes.h @@ -70,6 +70,11 @@ class CodeGenTypes { /// Contains the LLVM IR type for any converted RecordDecl. llvm::DenseMap RecordDeclTypes; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Contains the LLVM IR type for any converted wide pointer type. + llvm::DenseMap WidePointerTypes; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Hold memoized CGFunctionInfo results. llvm::FoldingSet FunctionInfos{FunctionInfosLog2InitSize}; @@ -145,6 +150,10 @@ class CodeGenTypes { /// load/store type are the same. llvm::Type *convertTypeForLoadStore(QualType T, llvm::Type *LLVMTy = nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::Type *ConvertBoundsSafetyPointerType(const PointerType *PT); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// GetFunctionType - Get the LLVM function type for \arg Info. llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info); diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index 67a9caf8b4ec4..e920615ed4d39 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -2343,6 +2343,31 @@ struct CounterCoverageMappingBuilder if (OVE->isUnique()) Visit(OVE->getSourceExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // In most expression types, only visit the main expressions, as the others + // weren't spelled out and can have confusing source locations. + void VisitAssumptionExpr(const AssumptionExpr *AE) { + Visit(AE->getWrappedExpr()); + } + + void VisitBoundsSafetyPointerPromotionExpr( + const BoundsSafetyPointerPromotionExpr *FPPE) { + Visit(FPPE->getPointer()); + } + + void VisitBoundsCheckExpr(const BoundsCheckExpr *BCE) { + Visit(BCE->getGuardedExpr()); + } + + void VisitPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *PBCE) { + Visit(PBCE->getGuardedExpr()); + } + + void VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *MSE) { + Visit(MSE->getWrappedExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index 1dc3172a6bdf9..6da8793785a6e 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -355,9 +355,10 @@ bool X86_32ABIInfo::shouldReturnTypeInRegister(QualType Ty, // If this is a builtin, pointer, enum, complex type, member pointer, or // member function pointer it is ok. - if (Ty->getAs() || Ty->hasPointerRepresentation() || - Ty->isAnyComplexType() || Ty->isEnumeralType() || - Ty->isBlockPointerType() || Ty->isMemberPointerType()) + if ((Ty->getAs() || Ty->hasPointerRepresentation() || + Ty->isAnyComplexType() || Ty->isEnumeralType() || + Ty->isBlockPointerType() || Ty->isMemberPointerType()) && + !Ty->isPointerTypeWithBounds()) return true; // Arrays are treated like records. @@ -1844,6 +1845,28 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo, return; } + // BoundsSafety wide pointers. + if (Ty->isPointerTypeWithBounds()) { + uint64_t Size = getContext().getTypeSize(Ty); + // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger + // than eight eightbytes, ..., it has class MEMORY. + assert(Size <= 512 && "Wide pointers won't be larger than 512 bytes."); + + // This is a summarized version of the RecordType routine such that the + // result be equivalent to having RecordDecl for a wide pointer. + // In effect, '__indexable' which only has an upper bound will be passed + // in register, while '__bidi_indexable' which has both upper and lower + // bounds will be passed in memory. + if (Size > 128) { + Lo = Memory; + } else { + Lo = Integer; + Hi = Integer; + } + postMerge(Size, Lo, Hi); + return; + } + if (Ty->hasPointerRepresentation()) { Current = Integer; return; diff --git a/clang/lib/Driver/BoundsSafetyArgs.cpp b/clang/lib/Driver/BoundsSafetyArgs.cpp new file mode 100644 index 0000000000000..de6b7a7a0e34c --- /dev/null +++ b/clang/lib/Driver/BoundsSafetyArgs.cpp @@ -0,0 +1,95 @@ +//===- BoundsSafetyArgs.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "clang/Driver/BoundsSafetyArgs.h" +#include "clang/Basic/DiagnosticDriver.h" +#include "clang/Driver/Options.h" + +using namespace llvm::opt; +using namespace clang::driver::options; + +namespace clang { + +namespace driver { + +BoundsSafetyNewChecksMaskTy +ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine *Diags) { + auto FilteredArgs = + Args.filtered(OPT_fbounds_safety_bringup_missing_checks_EQ, + OPT_fno_bounds_safety_bringup_missing_checks_EQ); + if (FilteredArgs.empty()) { + // No flags present. Use the default + return LangOptions::BS_CHK_Default; + } + + // If flags are present then start with BS_CHK_None as the initial mask and + // update the mask based on the flags. This preserves compiler behavior for + // users that adopted the `-fbounds-safety-bringup-missing-checks` flag when + // `BS_CHK_Default == BS_CHK_None`. + BoundsSafetyNewChecksMaskTy Result = LangOptions::BS_CHK_None; + // All the options will be applied as masks in the command line order, to make + // it easier to enable all but certain checks (or disable all but certain + // checks). + for (const Arg *A : FilteredArgs) { + for (const char *Value : A->getValues()) { + std::optional Mask = + llvm::StringSwitch< + std::optional>(Value) + .Case("access_size", LangOptions::BS_CHK_AccessSize) + .Case("indirect_count_update", + LangOptions::BS_CHK_IndirectCountUpdate) + .Case("return_size", LangOptions::BS_CHK_ReturnSize) + .Case("ended_by_lower_bound", + LangOptions::BS_CHK_EndedByLowerBound) + .Case("compound_literal_init", + LangOptions::BS_CHK_CompoundLiteralInit) + .Case("libc_attributes", LangOptions::BS_CHK_LibCAttributes) + .Case("all", LangOptions::BS_CHK_All) + .Case("none", LangOptions::BS_CHK_None) + .Default(std::nullopt); + if (!Mask) { + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << A->getSpelling() << Value; + break; + } + + bool IsPosFlag = + A->getOption().matches(OPT_fbounds_safety_bringup_missing_checks_EQ) + ? true + : false; + + // `-fbounds-safety-bringup-missing-checks=none` would do nothing as the + // masks are additive, which is unlikely to be intended. To disable all + // checks, `-fno-bounds-safety-bringup-missing-checks=all` should be used + // instead. Hence, "none" is not supported, triggering an error with the + // suggestion. + if (*Mask == LangOptions::BS_CHK_None) { + if (Diags) + Diags->Report(diag::err_drv_invalid_value_with_flag_suggestion) + << A->getSpelling() << Value + << (IsPosFlag ? "-fno-bounds-safety-bringup-missing-checks" + : "-fbounds-safety-bringup-missing-checks"); + break; + } + + if (IsPosFlag) { + Result |= *Mask; + } else { + assert(A->getOption().matches( + OPT_fno_bounds_safety_bringup_missing_checks_EQ)); + Result &= ~(*Mask); + } + } + } + return Result; +} + +} // namespace driver + +} // namespace clang diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index c14ca46214b86..95caf74d58d1e 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -17,6 +17,7 @@ endif() add_clang_library(clangDriver Action.cpp + BoundsSafetyArgs.cpp Compilation.cpp Distro.cpp Driver.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a64b4311d0ff4..66c25f4414867 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -6551,6 +6551,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, TC = std::make_unique(*this, Target, Args); else if (Target.isOSBinFormatELF()) TC = std::make_unique(*this, Target, Args); + else if (Target.isAppleMachO()) + TC = std::make_unique(*this, Target, Args); else if (Target.isOSBinFormatMachO()) TC = std::make_unique(*this, Target, Args); else diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 93d95c0dbd8a1..70456e46ff57b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7016,6 +7016,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job, } Args.AddLastArg(CmdArgs, options::OPT_ftrap_function_EQ); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (Arg *A = Args.getLastArg(options::OPT_ftrap_function_returns, + options::OPT_fno_trap_function_returns)) + if (A->getOption().matches(options::OPT_ftrap_function_returns)) { + if (Args.getLastArg(options::OPT_ftrap_function_EQ)) + CmdArgs.push_back("-ftrap-function-returns"); + else + D.Diag(diag::err_drv_option_requires_option) + << "-ftrap-function-returns" << "-ftrap-function="; + } + + Args.addOptInFlag(CmdArgs, options::OPT_funique_traps, + options::OPT_fno_unique_traps); + /* TO_UPSTREAM(BoundsSafety) OFF*/ // -fno-strict-overflow implies -fwrapv if it isn't disabled, but // -fstrict-overflow won't turn off an explicitly enabled -fwrapv. @@ -7489,6 +7503,58 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job, Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // -fbounds-safety is off by default. + if (Arg *A = Args.getLastArg(options::OPT_fbounds_safety, + options::OPT_fno_bounds_safety)) { + if (A->getOption().matches(options::OPT_fbounds_safety)) { + CmdArgs.push_back("-fbounds-safety"); + auto Iter = Args.filtered(options::OPT_mllvm); + bool HasBoundsCheckOpt = std::any_of(Iter.begin(), Iter.end(), [](const Arg *A) { + return StringRef(A->getValue(0)).starts_with("-enable-constraint-elimination"); + }); + // Turn on constraint-elimination pass when -fbounds-safety is enabled when the + // pass is not explicitly enabled or disabled. + if (!HasBoundsCheckOpt) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-enable-constraint-elimination"); + } + + // This is inside the -fbounds-safety flag checking, such that the flag + // will be ignored with -Wunused-command-line-argument being triggered. + Args.addAllArgs( + CmdArgs, {options::OPT_fbounds_safety_bringup_missing_checks_EQ, + options::OPT_fno_bounds_safety_bringup_missing_checks_EQ}); + } + } + + if (Args.hasFlag(options::OPT_fexperimental_bounds_safety_attributes, + options::OPT_fno_experimental_bounds_safety_attributes, + false)) { + CmdArgs.push_back("-fexperimental-bounds-safety-attributes"); + } + + if (Args.hasFlag(options::OPT_fbounds_attributes_cxx_experimental, + options::OPT_fno_bounds_attributes_cxx_experimental, + false)) { + CmdArgs.push_back("-fbounds-attributes-cxx-experimental"); + } + + if (Args.hasFlag(options::OPT_fbounds_attributes_objc_experimental, + options::OPT_fno_bounds_attributes_objc_experimental, + false)) { + CmdArgs.push_back("-fbounds-attributes-objc-experimental"); + } + + // -fbounds-safety-relaxed-system-headers is on by default + Args.addOptOutFlag(CmdArgs, + options::OPT_fbounds_safety_relaxed_system_headers, + options::OPT_fno_bounds_safety_relaxed_system_headers); + + Args.addOptInFlag(CmdArgs, options::OPT_fbounds_safety_adoption_mode, + options::OPT_fno_bounds_safety_adoption_mode); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // -fassume-unique-vtables is on by default. Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables, options::OPT_fno_assume_unique_vtables); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 959d29587f238..975885b8aabc3 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/AlignedAllocation.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Config/config.h" +#include "clang/Driver/BoundsSafetyArgs.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" @@ -1048,10 +1049,14 @@ MachO::MachO(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) getProgramPaths().push_back(getDriver().Dir); } +AppleMachO::AppleMachO(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args) + : MachO(D, Triple, Args), CudaInstallation(D, Triple, Args), + RocmInstallation(D, Triple, Args) {} + /// Darwin - Darwin tool chain for i386 and x86_64. Darwin::Darwin(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) - : MachO(D, Triple, Args), TargetInitialized(false), - CudaInstallation(D, Triple, Args), RocmInstallation(D, Triple, Args) {} + : AppleMachO(D, Triple, Args), TargetInitialized(false) {} types::ID MachO::LookupTypeForExtension(StringRef Ext) const { types::ID Ty = ToolChain::LookupTypeForExtension(Ext); @@ -1100,13 +1105,13 @@ bool Darwin::hasBlocksRuntime() const { } } -void Darwin::AddCudaIncludeArgs(const ArgList &DriverArgs, - ArgStringList &CC1Args) const { +void AppleMachO::AddCudaIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args); } -void Darwin::AddHIPIncludeArgs(const ArgList &DriverArgs, - ArgStringList &CC1Args) const { +void AppleMachO::AddHIPIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args); } @@ -1201,6 +1206,8 @@ VersionTuple MachO::getLinkerVersion(const llvm::opt::ArgList &Args) const { Darwin::~Darwin() {} +AppleMachO::~AppleMachO() {} + MachO::~MachO() {} std::string Darwin::ComputeEffectiveClangTriple(const ArgList &Args, @@ -2562,7 +2569,7 @@ static void AppendPlatformPrefix(SmallString<128> &Path, // Returns the effective sysroot from either -isysroot or --sysroot, plus the // platform prefix (if any). llvm::SmallString<128> -DarwinClang::GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const { +AppleMachO::GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const { llvm::SmallString<128> Path("/"); if (DriverArgs.hasArg(options::OPT_isysroot)) Path = DriverArgs.getLastArgValue(options::OPT_isysroot); @@ -2575,8 +2582,9 @@ DarwinClang::GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const { return Path; } -void DarwinClang::AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args) const { +void AppleMachO::AddClangSystemIncludeArgs( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { const Driver &D = getDriver(); llvm::SmallString<128> Sysroot = GetEffectiveSysroot(DriverArgs); @@ -2654,7 +2662,7 @@ bool DarwinClang::AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverAr return getVFS().exists(Base); } -void DarwinClang::AddClangCXXStdlibIncludeArgs( +void AppleMachO::AddClangCXXStdlibIncludeArgs( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { // The implementation from a base class will pass through the -stdlib to @@ -2711,55 +2719,60 @@ void DarwinClang::AddClangCXXStdlibIncludeArgs( } case ToolChain::CST_Libstdcxx: - llvm::SmallString<128> UsrIncludeCxx = Sysroot; - llvm::sys::path::append(UsrIncludeCxx, "usr", "include", "c++"); - - llvm::Triple::ArchType arch = getTriple().getArch(); - bool IsBaseFound = true; - switch (arch) { - default: break; - - case llvm::Triple::x86: - case llvm::Triple::x86_64: - IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, - "4.2.1", - "i686-apple-darwin10", - arch == llvm::Triple::x86_64 ? "x86_64" : ""); - IsBaseFound |= AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, - "4.0.0", "i686-apple-darwin8", - ""); - break; + AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args); + break; + } +} - case llvm::Triple::arm: - case llvm::Triple::thumb: - IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, - "4.2.1", - "arm-apple-darwin10", - "v7"); - IsBaseFound |= AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, - "4.2.1", - "arm-apple-darwin10", - "v6"); - break; +void AppleMachO::AddGnuCPlusPlusIncludePaths( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const {} - case llvm::Triple::aarch64: - IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, - "4.2.1", - "arm64-apple-darwin10", - ""); - break; - } +void DarwinClang::AddGnuCPlusPlusIncludePaths( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { + llvm::SmallString<128> UsrIncludeCxx = GetEffectiveSysroot(DriverArgs); + llvm::sys::path::append(UsrIncludeCxx, "usr", "include", "c++"); - if (!IsBaseFound) { - getDriver().Diag(diag::warn_drv_libstdcxx_not_found); - } + llvm::Triple::ArchType arch = getTriple().getArch(); + bool IsBaseFound = true; + switch (arch) { + default: + break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + IsBaseFound = AddGnuCPlusPlusIncludePaths( + DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", "i686-apple-darwin10", + arch == llvm::Triple::x86_64 ? "x86_64" : ""); + IsBaseFound |= AddGnuCPlusPlusIncludePaths( + DriverArgs, CC1Args, UsrIncludeCxx, "4.0.0", "i686-apple-darwin8", ""); + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + IsBaseFound = + AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", + "arm-apple-darwin10", "v7"); + IsBaseFound |= + AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", + "arm-apple-darwin10", "v6"); + break; + + case llvm::Triple::aarch64: + IsBaseFound = + AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", + "arm64-apple-darwin10", ""); break; } + + if (!IsBaseFound) { + getDriver().Diag(diag::warn_drv_libstdcxx_not_found); + } } -void DarwinClang::AddCXXStdlibLibArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { +void AppleMachO::AddCXXStdlibLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { CXXStdlibType Type = GetCXXStdlibType(Args); switch (Type) { @@ -3726,7 +3739,7 @@ SanitizerMask Darwin::getSupportedSanitizers() const { return Res; } -void Darwin::printVerboseInfo(raw_ostream &OS) const { +void AppleMachO::printVerboseInfo(raw_ostream &OS) const { CudaInstallation->print(OS); RocmInstallation->print(OS); } diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index f6a7537fc00ee..0835871b841d1 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -294,8 +294,49 @@ class LLVM_LIBRARY_VISIBILITY MachO : public ToolChain { /// } }; +/// Apple specific MachO extensions +class LLVM_LIBRARY_VISIBILITY AppleMachO : public MachO { +public: + AppleMachO(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + ~AppleMachO() override; + + /// } + /// @name Apple Specific ToolChain Implementation + /// { + void + AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + + void AddCudaIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + void AddHIPIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + + void AddClangCXXStdlibIncludeArgs( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + void AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const override; + + void printVerboseInfo(raw_ostream &OS) const override; + /// } + + LazyDetector CudaInstallation; + LazyDetector RocmInstallation; + +protected: + llvm::SmallString<128> + GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const; + +private: + virtual void + AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const; +}; + /// Darwin - The base Darwin tool chain. -class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { +class LLVM_LIBRARY_VISIBILITY Darwin : public AppleMachO { public: /// Whether the information on the target has been initialized. // @@ -333,9 +374,6 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { /// The target variant triple that was specified (if any). mutable std::optional TargetVariantTriple; - LazyDetector CudaInstallation; - LazyDetector RocmInstallation; - private: void AddDeploymentTarget(llvm::opt::DerivedArgList &Args) const; @@ -347,7 +385,7 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { std::string ComputeEffectiveClangTriple(const llvm::opt::ArgList &Args, types::ID InputType) const override; - /// @name Apple Specific Toolchain Implementation + /// @name Darwin Specific Toolchain Implementation /// { void addMinVersionArgs(const llvm::opt::ArgList &Args, @@ -563,11 +601,6 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { ObjCRuntime getDefaultObjCRuntime(bool isNonFragile) const override; bool hasBlocksRuntime() const override; - void AddCudaIncludeArgs(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args) const override; - void AddHIPIncludeArgs(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args) const override; - bool UseObjCMixedDispatch() const override { // This is only used with the non-fragile ABI and non-legacy dispatch. @@ -598,8 +631,6 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { bool SupportsEmbeddedBitcode() const override; SanitizerMask getSupportedSanitizers() const override; - - void printVerboseInfo(raw_ostream &OS) const override; }; /// DarwinClang - The Darwin toolchain used by Clang. @@ -617,16 +648,6 @@ class LLVM_LIBRARY_VISIBILITY DarwinClang : public Darwin { llvm::opt::ArgStringList &CmdArgs, bool ForceLinkBuiltinRT = false) const override; - void AddClangCXXStdlibIncludeArgs( - const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args) const override; - - void AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args) const override; - - void AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &CmdArgs) const override; - void AddCCKextLibArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const override; @@ -655,15 +676,16 @@ class LLVM_LIBRARY_VISIBILITY DarwinClang : public Darwin { StringRef Sanitizer, bool shared = true) const; + void + AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + bool AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, llvm::SmallString<128> Base, llvm::StringRef Version, llvm::StringRef ArchDir, llvm::StringRef BitDir) const; - - llvm::SmallString<128> - GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const; }; } // end namespace toolchains diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index 81797c8c4dc75..f878552fd42ea 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1081,6 +1081,11 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_MatrixCast: return false; + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: + llvm_unreachable("BoundsSafety extension is disabled for Objective-C"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_BooleanToSignedIntegral: llvm_unreachable("OpenCL-specific cast in Objective-C?"); diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index 030509d378759..85e99e17fbf70 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -1068,9 +1068,8 @@ void SymbolGraphSerializer::serializeWithExtensionGraphs( for (auto &ExtensionSGF : Serializer.ExtendedModules) { if (auto ExtensionOS = - CreateOutputStream(ExtensionSGF.getKey() + "@" + API.ProductName)) - Serializer.serializeGraphToStream(*ExtensionOS, Options, - ExtensionSGF.getKey(), + CreateOutputStream(API.ProductName + "@" + ExtensionSGF.getKey())) + Serializer.serializeGraphToStream(*ExtensionOS, Options, API.ProductName, std::move(ExtensionSGF.getValue())); } } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index fdfdbea6efea1..fcf0e8fb09a56 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -29,6 +29,7 @@ #include "clang/Basic/XRayInstr.h" #include "clang/CAS/IncludeTree.h" #include "clang/Config/config.h" +#include "clang/Driver/BoundsSafetyArgs.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" @@ -720,6 +721,13 @@ static bool FixupInvocation(CompilerInvocation &Invocation, << A->getSpelling() << T.getTriple(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!LangOpts.BoundsSafety && + Invocation.getDiagnosticOpts().BoundsSafetyAdoptionMode) { + Diags.Report(diag::warn_bounds_safety_adoption_mode_ignored); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Diags.getNumErrors() == NumErrorsBefore; } @@ -3908,6 +3916,32 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_ftrigraphs); } + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (Opts.BoundsSafety && + Opts.BoundsSafetyBringUpMissingChecks != LangOptions::BS_CHK_Default) { + std::string Checks; + if (Opts.BoundsSafetyBringUpMissingChecks == LangOptions::BS_CHK_All) { + Checks = "all"; + } else { + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_AccessSize)) + Checks += "access_size,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_IndirectCountUpdate)) + Checks += "indirect_count_update,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) + Checks += "return_size,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_EndedByLowerBound)) + Checks += "ended_by_lower_bound,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_CompoundLiteralInit)) + Checks += "compound_literal_init,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_LibCAttributes)) + Checks += "libc_attributes,"; + if (StringRef(Checks).ends_with(",")) + Checks.pop_back(); + } + GenerateArg(Consumer, OPT_fbounds_safety_bringup_missing_checks_EQ, Checks); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200)) GenerateArg(Consumer, OPT_fblocks); @@ -4315,6 +4349,71 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.Trigraphs = Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Parse the enabled checks and emit any necessary diagnostics + Opts.BoundsSafetyBringUpMissingChecks = + ParseBoundsSafetyNewChecksMaskFromArgs(Args, &Diags); + + // -fbounds-safety should be automatically marshalled into `Opts` in + // GenerateFrontendArgs() via `LangOpts<"BoundsSafety">` on + // `defm bounds_safety` in `clang/Driver/Options.td`. + + if (Opts.BoundsSafety) { + // If BoundsSafety is enabled, check that it is for a supported language. + // Currently, this is only C in languages that Clang has to parse. However, + // it's also possible to pass assembly files and LLVM IR through Clang, and + // those should be trivially supported. This is especially important because + // some build systems, like xcbuild and somewhat clumsy Makefiles, will pass + // C_FLAGS to Clang while building assembly files. + bool SupportsBoundsSafety = false; + switch (IK.getLanguage()) { + case Language::Unknown: + case Language::Asm: + case Language::LLVM_IR: + case Language::C: + SupportsBoundsSafety = true; + break; + case Language::ObjC: + SupportsBoundsSafety = Opts.BoundsAttributesObjCExperimental; + break; + case Language::CXX: + SupportsBoundsSafety = Opts.BoundsAttributesCXXExperimental; + break; + default: + break; + } + if (!SupportsBoundsSafety) + Diags.Report(diag::error_bounds_safety_lang_not_supported); + } + + bool IsBoundsSafetyAttributesExplicitlyDisabled = + !Args.hasFlag(OPT_fexperimental_bounds_safety_attributes, + OPT_fno_experimental_bounds_safety_attributes, true); + if (Opts.BoundsSafety && IsBoundsSafetyAttributesExplicitlyDisabled) + Diags.Report(diag::err_bounds_safety_attributes_cannot_be_disabled); + + if (Opts.BoundsSafety) { + // BoundsSafety implies BoundsSafetyAttributes. + Opts.BoundsSafetyAttributes = 1; + } + + if (Opts.BoundsSafetyAttributes) { + // Have -fbounds-safety silently imply + // `-fexperimental-late-parse-attributes`. This is kind of a hack because it + // won't be visible in the command line invocation. + Opts.ExperimentalLateParseAttributes = 1; + } + + if (Opts.BoundsAttributesCXXExperimental && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_attributes_cxx_experimental_ignored); + + if (Opts.BoundsAttributesObjCExperimental && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_attributes_objc_experimental_ignored); + + if (!Opts.BoundsSafetyRelaxedSystemHeaders && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_safety_relaxed_system_headers_ignored); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL && Opts.OpenCLVersion == 200); diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 651c55aeed540..7ffea4e654cd7 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -118,6 +118,10 @@ class MultiplexASTMutationListener : public ASTMutationListener { void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + /* TO_UPSTREAM(BoundsSafety) ON*/ + void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) override; + /* TO_UPSTREAM(BoundsSafety) OFF*/ void EnteringModulePurview() override; void AddedManglingNumber(const Decl *D, unsigned) override; void AddedStaticLocalNumbers(const Decl *D, unsigned) override; @@ -246,6 +250,14 @@ void MultiplexASTMutationListener::AddedAttributeToRecord( L->AddedAttributeToRecord(Attr, Record); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void MultiplexASTMutationListener::LazyAttributeToDecl(const Attr *Attr, + const Decl *D) { + for (auto *L : Listeners) + L->LazyAttributeToDecl(Attr, D); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void MultiplexASTMutationListener::EnteringModulePurview() { for (auto *L : Listeners) L->EnteringModulePurview(); diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 0592423c12eca..beb96f68ef5c8 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -184,6 +184,11 @@ class PrintPPOutputPPCallbacks : public PPCallbacks { void PragmaAssumeNonNullBegin(SourceLocation Loc) override; void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + /* TO_UPSTREAM(BoundsSafety) ON */ + void PragmaAbiPointerAttributesSet( + SourceLocation Loc, ArrayRef Spec) override; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Insert whitespace before emitting the next token. /// /// @param Tok Next token to be emitted. @@ -752,6 +757,25 @@ PragmaAssumeNonNullEnd(SourceLocation Loc) { setEmittedDirectiveOnThisLine(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void PrintPPOutputPPCallbacks:: +PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) { + startNewLineIfNeeded(); + MoveToLine(Loc, /*RequireStartOfLine=*/true); + *OS << "#pragma clang abi_ptr_attr set("; + bool IsFirst = true; + for (const IdentifierInfo *Str : Spec) { + if (!IsFirst) + *OS << ' '; + *OS << Str->getName(); + IsFirst = false; + } + *OS << ')'; + setEmittedDirectiveOnThisLine(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok, bool RequireSpace, bool RequireSameLine) { diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 83bd712ce14bd..57c3f73786aa7 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -230,6 +230,19 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { Clang->LoadRequestedPlugins(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // XXX: Is it already enabled by default? + if (Clang->getLangOpts().BoundsSafety) { + auto &LLVMArgs = Clang->getFrontendOpts().LLVMArgs; + bool HasConstraintElim = std::any_of(LLVMArgs.begin(), LLVMArgs.end(), + [](std::string &arg) { + return StringRef(arg).starts_with("-enable-constraint-elimination"); + }); + if (!HasConstraintElim) + LLVMArgs.push_back("-enable-constraint-elimination"); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // Honor -mllvm. // // FIXME: Remove this, one day. diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 89fa0ecd45eb4..c86aabe70c16f 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -286,6 +286,9 @@ set(files ${utility_files} ) +# TO_UPSTREAM(BoundsSafety) +list(APPEND files ptrcheck.h) + set(cuda_wrapper_files cuda_wrappers/algorithm cuda_wrappers/cmath diff --git a/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap index 9ffc249c8d1a2..fe72181485f9a 100644 --- a/clang/lib/Headers/module.modulemap +++ b/clang/lib/Headers/module.modulemap @@ -328,3 +328,10 @@ module ptrauth { header "ptrauth.h" export * } + +/* TO_UPSTREAM(BoundsSafety) ON */ +module ptrcheck { + header "ptrcheck.h" + export * +} +/* TO_UPSTREAM(BoundsSafety) OFF */ \ No newline at end of file diff --git a/clang/lib/Headers/ptrcheck.h b/clang/lib/Headers/ptrcheck.h new file mode 100644 index 0000000000000..6a6bb71df7b91 --- /dev/null +++ b/clang/lib/Headers/ptrcheck.h @@ -0,0 +1,339 @@ +/*===---- ptrcheck.h - Pointer bounds hints & specifications ----------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __PTRCHECK_H +#define __PTRCHECK_H + +/* __has_ptrcheck can be used in preprocessor macros (and other parts of the + language expecting constant expressions) to test if bounds attributes + exist. */ +#if defined(__has_feature) && __has_feature(bounds_attributes) + #define __has_ptrcheck 1 +#else + #define __has_ptrcheck 0 +#endif + +#if defined(__has_feature) && __has_feature(bounds_safety_attributes) + #define __has_bounds_safety_attributes 1 +#else + #define __has_bounds_safety_attributes 0 +#endif + +#if __has_ptrcheck || __has_bounds_safety_attributes + +/* An attribute that modifies a pointer type such than it has the ABI of a + regular C pointer, without allowing pointer arithmetic. Pointer arithmetic is + a compile-time error. A __single pointer is expected to be either NULL or + point to exactly one valid value. */ +#define __single __attribute__((__single__)) + +/* An attribute that modifies a pointer type such than it can be used exactly + like a regular C pointer, with unchecked arithmetic and dereferencing. An + __unsafe_indexable pointer cannot convert implicitly to another type of + pointer since that would require information that is not available to the + program. You must use __unsafe_forge_bidi_indexable or __unsafe_forge_single + to convert __unsafe_indexable pointers to so-called safe pointers. */ +#define __unsafe_indexable __attribute__((__unsafe_indexable__)) + +/* An attribute that modifies a pointer type such that it has the ABI of a + regular C pointer, but it implicitly converts to a __bidi_indexable pointer + with bounds that assume there are N valid elements starting at its address. + The conversion happens at the same point the object converts to an rvalue, or + immediately for values which cannot be lvalues (such as function calls). */ + +/* Assignments to the pointer object must be accompanied with an assignment to + N if it is assignable. */ + +/* N must either be an expression that evaluates to a constant, or an integer + declaration from the same scope, or (for structure fields) a declaration + contained in basic arithmetic. */ +#define __counted_by(N) __attribute__((__counted_by__(N))) + +/* Identical to __counted_by_or_null(N), aside that the pointer may be null for + * non-zero values of N. */ +#define __counted_by_or_null(N) __attribute__((__counted_by_or_null__(N))) + +/* Identical to __counted_by(N), aside that N is a byte count instead of an + object count. */ +#define __sized_by(N) __attribute__((__sized_by__(N))) + +/* Identical to __sized_by(N), aside that the pointer may be null for non-zero + * values of N. */ +#define __sized_by_or_null(N) __attribute__((__sized_by_or_null__(N))) + +/* An attribute that modifies a pointer type such that it has the ABI of a + regular C pointer, but it implicitly converts to a __bidi_indexable pointer + with bounds that assume that E is one-past-the-end of the original object. + Implicitly, referencing E in the same scope will create a pointer that + converts to a __bidi_indexable pointer one-past-the-end of the original + object, but with a lower bound set to the value of the pointer that is + attributed. */ + +/* Assignments to the pointer object must be accompanied with an assignment to + E if it is assignable. */ +#define __ended_by(E) __attribute__((__ended_by__(E))) + +/* The __terminated_by(T) attribute can be applied to arrays and pointers. The + argument T specifies the terminator and must be an integer constant + expression. Even though T has to be an integer constant, __terminated_by(T) + can be applied to pointer arrays as well. For convenience, the + __null_terminated macro is provided, which is equivalent to + __terminated_by(0). + + The __terminated_by(T) attribute can be applied only to __single pointers. If + the pointer attribute is not specified, it is automatically set to __single. + A __terminated_by(T) pointer points to the first element of an array that is + terminated with T. + + Arithmetic on __terminated_by(T) pointers is restricted to only incrementing + the pointer by one, and must be able to be evaluated at compile-time. + Pointer arithmetic generates a runtime check to ensure that the pointer + doesn't point pass the terminator. + + A __terminated_by(T) pointer has the ABI of a regular C pointer. + + When __terminated_by(T) is applied to an array, the compiler checks if the + array is terminated with the given terminator T during the initialization. + Moreover, a __terminated_by(T) array decays to a __terminated_by(T) __single + pointer, instead of decaying to a __bidi_indexable pointer. */ +#define __terminated_by(T) __attribute__((__terminated_by__(T))) +#define __null_terminated __terminated_by(0) + +/* Directives that tells the compiler to assume that subsequent pointer types + have the ABI specified by the ABI parameter, which may be one of single, + indexable, bidi_indexable or unsafe_indexable. */ + +/* In project files, the ABI is assumed to be single by default. In headers + included from libraries or the SDK, the ABI is assumed to be unsafe_indexable + by default. */ +#define __ptrcheck_abi_assume_single() \ + _Pragma("clang abi_ptr_attr set(single)") + +#define __ptrcheck_abi_assume_unsafe_indexable() \ + _Pragma("clang abi_ptr_attr set(unsafe_indexable)") + +/* Create a __single pointer of a given type (T), starting at address P. T must + be a pointer type. */ +#define __unsafe_forge_single(T, P) \ + ((T __single)__builtin_unsafe_forge_single((P))) + +/* Create a __terminated_by pointer of a given pointer type (T), starting at + address P, terminated by E. T must be a pointer type. */ +#define __unsafe_forge_terminated_by(T, P, E) \ + ((T __terminated_by(E))__builtin_unsafe_forge_terminated_by((P), (E))) + +/* Create a __terminated_by pointer of a given pointer type (T), starting at + address P, terminated by 0. T must be a pointer type. */ +#define __unsafe_forge_null_terminated(T, P) __unsafe_forge_terminated_by(T, P, 0) + +/* Instruct the compiler to disregard the bounds of an array used in a function + prototype and allow the decayed pointer to use __counted_by. This is a niche + capability that is only useful in limited patterns (the way that `mig` uses + arrays being one of them). */ +#define __array_decay_discards_count_in_parameters \ + __attribute__((__decay_discards_count_in_parameters__)) + +/* An attribute to indicate a variable to be effectively constant (or data const) + that it is allocated in a const section so cannot be modified after an early + stage of bootup, for example. Adding this attribute allows a global variable + to be used in __counted_by attribute of struct fields, function parameter, or + local variable just like actual constants. + Note that ensuring the value never changes once it is used is the user's + responsibility. One way to achieve this is the xnu model, in which certain + variables are placed in a segment that is remapped as read-only after + initialization. */ +#define __unsafe_late_const __attribute__((__unsafe_late_const__)) + +#else + +#define __single +#define __unsafe_indexable +#define __counted_by(N) +#define __counted_by_or_null(N) +#define __sized_by(N) +#define __sized_by_or_null(N) +#define __ended_by(E) + +/* We intentionally define the terminated_by attributes to nothing. */ +#define __terminated_by(T) +#define __null_terminated + +/* Similarly, we intentionally define to nothing the + __ptrcheck_abi_assume_single and __ptrcheck_abi_assume_unsafe_indexable + macros because they do not lead to an ABI incompatibility. However, we do not + define the indexable and unsafe_indexable ones because the diagnostic is + better than the silent ABI break. */ +#define __ptrcheck_abi_assume_single() +#define __ptrcheck_abi_assume_unsafe_indexable() + +/* __unsafe_forge intrinsics are defined as regular C casts. */ +#define __unsafe_forge_single(T, P) ((T)(P)) +#define __unsafe_forge_terminated_by(T, P, E) ((T)(P)) +#define __unsafe_forge_null_terminated(T, P) ((T)(P)) + +/* decay operates normally; attribute is meaningless without pointer checks. */ +#define __array_decay_discards_count_in_parameters + +#endif /* __has_ptrcheck || __has_bounds_safety_attributes */ + +#if __has_ptrcheck + +/* An attribute that modifies a pointer type such that its ABI is three pointer + components: the pointer value itself (the pointer value); one-past-the-end of + the object it is derived from (the upper bound); and the base address of the + object it is derived from (the lower bound). The pointer value is allowed to + lie outside the [lower bound, upper bound) interval, and it supports the + entire range of arithmetic operations that are usually applicable to + pointers. Bounds are implicitly checked only when the pointer is dereferenced + or converted to a different representation. */ +#define __bidi_indexable __attribute__((__bidi_indexable__)) + +/* An attribute that modifies a pointer type such that its ABI is two pointer + components: the pointer value itself (the lower bound); and one-past-the-end + of the object it is derived from (the upper bound). Indexable pointers do not + support negative arithmetic operations: it is a compile-time error to use a + subtraction or add a negative quantity to them, and it is a runtime error if + the same happens at runtime while it can't be detected at compile-time. Same + as __bidi_indexable pointers, __indexable pointers are bounds-checked when + dereferenced or converted to another representation. */ +#define __indexable __attribute__((__indexable__)) + +/* Directives that tells the compiler to assume that subsequent pointer types + have the ABI specified by the ABI parameter, which may be one of single, + indexable, bidi_indexable or unsafe_indexable. */ + +#define __ptrcheck_abi_assume_indexable() \ + _Pragma("clang abi_ptr_attr set(indexable)") + +#define __ptrcheck_abi_assume_bidi_indexable() \ + _Pragma("clang abi_ptr_attr set(bidi_indexable)") + +/* Create a __bidi_indexable pointer of a given pointer type (T), starting at + address P, pointing to S bytes of valid memory. T must be a pointer type. */ +#define __unsafe_forge_bidi_indexable(T, P, S) \ + ((T __bidi_indexable)__builtin_unsafe_forge_bidi_indexable((P), (S))) + +/* Create a wide pointer with the same lower bound and upper bounds as X, but + with a pointer component also equal to the lower bound. */ +#define __ptr_lower_bound(X) __builtin_get_pointer_lower_bound(X) + +/* Create a wide pointer with the same lower bound and upper bounds as X, but + with a pointer component also equal to the upper bound. */ +#define __ptr_upper_bound(X) __builtin_get_pointer_upper_bound(X) + +/* Convert a __terminated_by(T) pointer to an __indexable pointer. These + operations will calculate the upper bound by iterating over the memory + pointed to by P in order to find the terminator. + + The __terminated_by_to_indexable(P) does NOT include the terminator within + bounds of the __indexable pointer. Consequently, the terminator cannot be + erased (or even accessed) through the __indexable pointer. The address one + past the end of the array (pointing to the terminator) can be found with + __ptr_upper_bound(). + + The __unsafe_terminated_by_to_indexable(P) does include the terminator within + the bounds of the __indexable pointer. This makes the operation unsafe, since + the terminator can be erased, and thus using P might result in out-of-bounds + access. */ +#define __terminated_by_to_indexable(P) \ + __builtin_terminated_by_to_indexable(P) +#define __unsafe_terminated_by_to_indexable(P) \ + __builtin_unsafe_terminated_by_to_indexable(P) +#define __null_terminated_to_indexable(P) \ + __builtin_terminated_by_to_indexable((P), 0) +#define __unsafe_null_terminated_to_indexable(P) \ + __builtin_unsafe_terminated_by_to_indexable((P), 0) + +/* __unsafe_terminated_by_from_indexable(T, PTR [, PTR_TO_TERM]) converts an + __indexable pointer to a __terminated_by(T) pointer. The operation will + check if the given terminator T occurs in the memory pointed to by PTR. + If so, the operation evaluates to __terminated_by(T) pointer. Otherwise, it + traps. + + The operation has an optional parameter PTR_TO_TERM, which changes the way + how the check for the terminator existence is generated. PTR_TO_TERM must + point to the terminator element and be within the bounds of PTR. + If PTR_TO_TERM is provided, the runtime will check if it is in fact within + the bounds and points to an element that equals to T. If PTR_TO_TERM is not + provided, the runtime will iterate over the memory pointed to by PTR to find + the terminator. + + The operation is unsafe, since the terminator can be erased through PTR after + the conversion. This can result in out-of-bounds access through the newly + created __terminated_by(T) pointer. + + For convenience, the + __unsafe_null_terminated_from_indexable(PTR [, PTR_TO_TERM]) macro is + provided, which assumes that the terminator is 0. */ +#define __unsafe_terminated_by_from_indexable(T, ...) \ + __builtin_unsafe_terminated_by_from_indexable((T), __VA_ARGS__) +#define __unsafe_null_terminated_from_indexable(...) \ + __builtin_unsafe_terminated_by_from_indexable(0, __VA_ARGS__) + +/* An attribute to indicate that a function is unavailable when -fbounds-safety + is enabled because it is unsafe. Calls to functions annotated with this + attribute are errors when -fbounds-safety is enabled, but are allowed when + -fbounds-safety is disabled. + + Example: + + void* __ptrcheck_unavailable some_unsafe_api(void*); + */ +#define __ptrcheck_unavailable \ + __attribute__((__unavailable__("unavailable with -fbounds-safety."))) + +/* __ptrcheck_unavailable_r is the same as __ptrcheck_unavailable but it takes + as an argument the name of replacement function that is safe for use with + -fbounds-safety enabled. + + Example: + + void* __counted_by(size) safe_api(void* __counted_by(size), size_t size); + + void* __ptrcheck_unavailable_r(safe_api) some_unsafe_api(void*); + */ +#define __ptrcheck_unavailable_r(REPLACEMENT) \ + __attribute__((__unavailable__( \ + "unavailable with -fbounds-safety. Use " #REPLACEMENT " instead."))) + +#else + +/* We intentionally define to nothing pointer attributes which do not have an + impact on the ABI. __indexable and __bidi_indexable are not defined because + of the ABI incompatibility that makes the diagnostic preferable. */ + +/* __unsafe_forge intrinsics are defined as regular C casts. */ +#define __unsafe_forge_bidi_indexable(T, P, S) ((T)(P)) + +/* The conversion between terminated_by pointers just evaluates to the pointer + argument. */ +#define __terminated_by_to_indexable(P) (P) +#define __unsafe_terminated_by_to_indexable(P) (P) +#define __null_terminated_to_indexable(P) (P) +#define __unsafe_null_terminated_to_indexable(P) (P) + +#define __IGNORE_REST(P, ...) (P) +/* Adding DUMMY is a workaround for the case where the macro is called + with only a single argument: calling the __IGNORE_REST macro with only a + single argument is a C23 extension and emits a warning for earlier + versions. + */ +#define __unsafe_terminated_by_from_indexable(T, ...) \ + __IGNORE_REST(__VA_ARGS__, DUMMY) +#define __unsafe_null_terminated_from_indexable(...) \ + __unsafe_terminated_by_from_indexable(DUMMY_TYPE, __VA_ARGS__) + +/* The APIs marked with these attributes are available outside the context of + pointer checks, so do nothing. */ +#define __ptrcheck_unavailable +#define __ptrcheck_unavailable_r(REPLACEMENT) + +#endif /* __has_ptrcheck */ + +#endif /* __PTRCHECK_H */ diff --git a/clang/lib/Lex/InitHeaderSearch.cpp b/clang/lib/Lex/InitHeaderSearch.cpp index cb3941fa94821..0993d3a653485 100644 --- a/clang/lib/Lex/InitHeaderSearch.cpp +++ b/clang/lib/Lex/InitHeaderSearch.cpp @@ -315,7 +315,7 @@ bool InitHeaderSearch::ShouldAddDefaultIncludePaths( break; case llvm::Triple::UnknownOS: - if (triple.isWasm()) + if (triple.isWasm() || triple.isAppleMachO()) return false; break; diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 6ec63b91df4be..cbbb085338359 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1974,6 +1974,50 @@ struct PragmaAssumeNonNullHandler : public PragmaHandler { } }; +/* TO_UPSTREAM(BoundsSafety) ON*/ +struct PragmaAbiPointerAttributesHandler : public PragmaHandler { + PragmaAbiPointerAttributesHandler() : PragmaHandler("abi_ptr_attr") {} + + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &NameTok) override { + SourceLocation Loc = NameTok.getLocation(); + PPCallbacks *Callbacks = PP.getPPCallbacks(); + Token Tok; + + // Lex the mode. + PP.LexUnexpandedToken(Tok); + const IdentifierInfo *Mode = Tok.getIdentifierInfo(); + if (!Mode || !Mode->isStr("set")) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + // Parse '(', arbitrary list of identifiers, ')' + PP.LexUnexpandedToken(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + SmallVector Attributes; + for (PP.LexUnexpandedToken(Tok); + const IdentifierInfo *II = Tok.getIdentifierInfo(); + PP.LexUnexpandedToken(Tok)) + Attributes.push_back(II); + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + PP.LexUnexpandedToken(Tok); + if (Tok.isNot(tok::eod)) + PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma"; + Callbacks->PragmaAbiPointerAttributesSet(Loc, Attributes); + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Handle "\#pragma region [...]" /// /// The syntax is @@ -2154,6 +2198,9 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang")); AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler()); AddPragmaHandler("clang", new PragmaAssumeNonNullHandler()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + AddPragmaHandler("clang", new PragmaAbiPointerAttributesHandler()); + /* TO_UPSTREAM(BoundsSafety) OFF*/ AddPragmaHandler("clang", new PragmaDeprecatedHandler()); AddPragmaHandler("clang", new PragmaRestrictExpansionHandler()); AddPragmaHandler("clang", new PragmaFinalHandler()); diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index eaf6328e47649..1067e9caef0b2 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -836,6 +836,19 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA, Diag(Tok, diag::warn_attribute_on_function_definition) << &LA.AttrName; + /* TO_UPSTREAM(BoundsSafety) ON */ + const auto &SM = PP.getSourceManager(); + CharSourceRange ExpansionRange = SM.getExpansionRange(LA.AttrNameLoc); + StringRef FoundName = + Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()) + .split('(') + .first; + IdentifierInfo *MacroII = PP.getIdentifierInfo(FoundName); + for (unsigned i = 0; i < Attrs.size(); ++i) + Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin(), + SM.isInSystemMacro(LA.AttrNameLoc)); + /* TO_UPSTREAM(BoundsSafety) OFF */ + for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index deb8109a8e3dd..8c51ca6561cf8 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -49,7 +49,9 @@ using namespace clang; /// Called type-id in C++. TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context, AccessSpecifier AS, Decl **OwnedType, - ParsedAttributes *Attrs) { + ParsedAttributes *Attrs, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context); if (DSC == DeclSpecContext::DSC_normal) DSC = DeclSpecContext::DSC_type_specifier; @@ -80,6 +82,11 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context, if (Range) *Range = DeclaratorInfo.getSourceRange(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LateAttrs) + DistributeCLateParsedAttrs(DeclaratorInfo, nullptr, LateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (DeclaratorInfo.isInvalidType()) return true; @@ -297,11 +304,17 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs, FindLocsWithCommonFileID(PP, AttrTokLoc, Loc)) { CharSourceRange ExpansionRange = SM.getExpansionRange(AttrTokLoc); StringRef FoundName = - Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()); + Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()) + /* TO_UPSTREAM(BoundsSafety) ON */ + .split('(') + .first; + /* TO_UPSTREAM(BoundsSafety) OFF */ IdentifierInfo *MacroII = PP.getIdentifierInfo(FoundName); for (unsigned i = OldNumAttrs; i < Attrs.size(); ++i) - Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin()); + Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin(), + // TO_UPSTREAM(BoundsSafety) + SM.isInSystemMacro(AttrTokLoc)); if (LateAttrs) { for (unsigned i = OldNumLateAttrs; i < LateAttrs->size(); ++i) @@ -666,10 +679,14 @@ unsigned Parser::ParseAttributeArgsCommon( /// Parse the arguments to a parameterized GNU attribute or /// a C++11 attribute in "gnu" namespace. +/* TO_UPSTREAM(BoundsSafety) ON */ +// NestedTypeLevel parameter isn't present in upstream void Parser::ParseGNUAttributeArgs( IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, - SourceLocation ScopeLoc, ParsedAttr::Form Form, Declarator *D) { + SourceLocation ScopeLoc, ParsedAttr::Form Form, Declarator *D, + size_t NestedTypeLevel) { + /* TO_UPSTREAM(BoundsSafety) OFF */ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); @@ -700,12 +717,19 @@ void Parser::ParseGNUAttributeArgs( ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, Form); return; + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (AttrKind == ParsedAttr::AT_PtrEndedBy) { + ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, + Form, NestedTypeLevel); + return; + /* TO_UPSTREAM(BoundsSafety) OFF */ } else if (AttrKind == ParsedAttr::AT_CountedBy || AttrKind == ParsedAttr::AT_CountedByOrNull || AttrKind == ParsedAttr::AT_SizedBy || AttrKind == ParsedAttr::AT_SizedByOrNull) { + // TO_UPSTREAM(BoundsSafety): `NestedTypeLevel` is not passed in upstream ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, - Form); + Form, NestedTypeLevel); return; } else if (AttrKind == ParsedAttr::AT_CXXAssume) { ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form); @@ -2100,7 +2124,13 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( ParsedTemplateInfo TemplateInfo; DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); - ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext); + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: Why is PSoon true? + LateParsedAttrList BoundsSafetyLateAttrs( + /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true); + ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext, + &BoundsSafetyLateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. @@ -2132,7 +2162,9 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); - return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd, FRI); + return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd, FRI, + // TO_UPSTREAM(BoundsSafety) + &BoundsSafetyLateAttrs); } /// Returns true if this might be the start of a declarator, or a common typo @@ -2291,7 +2323,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParsedAttributes &Attrs, ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd, - ForRangeInit *FRI) { + ForRangeInit *FRI, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs) { // Parse the first declarator. // Consume all of the attributes from `Attrs` by moving them to our own local // list. This ensures that we will not attempt to interpret them as statement @@ -2400,7 +2434,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // and recover by ignoring the 'template' keyword. Diag(Tok, diag::err_template_defn_explicit_instantiation) << 0; TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(), - &LateParsedAttrs); + &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } else { SourceLocation LAngleLoc = PP.getLocForEndOfToken(TemplateInfo.TemplateLoc); @@ -2420,11 +2456,15 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParsedTemplateInfo(&FakedParamLists, /*isSpecialization=*/true, /*lastParameterListWasEmpty=*/true), - &LateParsedAttrs); + &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } } else { TheDecl = - ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs); + ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } return Actions.ConvertDeclToDeclGroup(TheDecl); @@ -2514,6 +2554,19 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, SmallVector DeclsInGroup; Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo, FRI); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Make sure unrelated attributes are not silently picked up by + // -fbounds-safety late parsing logic and ignored. + assert(getLangOpts().BoundsSafetyAttributes || (!BoundsSafetyLateAttrs || BoundsSafetyLateAttrs->size() == 0)); + + if (getLangOpts().BoundsSafetyAttributes && BoundsSafetyLateAttrs) { + DistributeCLateParsedAttrs(D, FirstDecl, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false); D.complete(FirstDecl); @@ -2576,6 +2629,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, if (Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && BoundsSafetyLateAttrs) { + DistributeCLateParsedAttrs(D, ThisDecl, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + D.complete(ThisDecl); if (ThisDecl) DeclsInGroup.push_back(ThisDecl); @@ -2914,12 +2975,23 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( /// void Parser::ParseSpecifierQualifierList( DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, - AccessSpecifier AS, DeclSpecContext DSC) { - ParsedTemplateInfo TemplateInfo; + AccessSpecifier AS, DeclSpecContext DSC, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { /// specifier-qualifier-list is a subset of declaration-specifiers. Just /// parse declaration-specifiers and complain about extra stuff. /// TODO: diagnose attribute-specifiers and alignment-specifiers. - ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, nullptr, + + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + + ParsedTemplateInfo TemplateInfo; + // TO_UPSTREAM(BoundsSafety): Pass LateAttrs + ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs, AllowImplicitTypename); // Validate declspec for type-name. @@ -3387,8 +3459,8 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) { /*IsRegularKeywordAttribute=*/false)); } - -void Parser::DistributeCLateParsedAttrs(Decl *Dcl, +// TO_UPSTREAM(BoundsSafety): Declator &D isn't passed in upstream +void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, LateParsedAttrList *LateAttrs) { if (!LateAttrs) return; @@ -3399,6 +3471,58 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl, LateAttr->addDecl(Dcl); } } + + /* TO_UPSTREAM(BoundsSafety) ON */ + unsigned NestedLevel = 0; + struct FunctionChunkInfo { + bool Valid = false; + unsigned Index = 0; + } FuncInfo; + std::unique_ptr ProtoScope; + LateParsedAttrList ProtoLateAttrs(true); + + for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) { + DeclaratorChunk &DC = D.getTypeObject(i); + + ArrayRef LPAI; + switch (DC.Kind) { + case DeclaratorChunk::Pointer: + LPAI = DC.Ptr.getLateParsedAttrInfos(); + break; + case DeclaratorChunk::Array: + LPAI = DC.Arr.getLateParsedAttrInfos(); + break; + case DeclaratorChunk::Function: + FuncInfo.Index = i; + FuncInfo.Valid = true; + continue; + default: + continue; + } + + if (!LPAI.empty() && FuncInfo.Valid && !ProtoScope) { + const DeclaratorChunk &FuncChunk = D.getTypeObject(FuncInfo.Index); + ProtoScope.reset(new ParseScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope)); + const DeclaratorChunk::FunctionTypeInfo &FTI = FuncChunk.Fun; + for (unsigned i = 0; i != FTI.NumParams; ++i) { + ParmVarDecl *Param = cast(FTI.Params[i].Param); + Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param); + } + LateAttrs = &ProtoLateAttrs; + } + for (const auto *LI : LPAI) { + LateParsedAttribute *LA = new LateParsedAttribute( + this, LI->AttrName, LI->AttrNameLoc, NestedLevel); + LA->Toks = std::move(LI->Toks); + if (Dcl) + LA->addDecl(Dcl); + LateAttrs->push_back(LA); + } + NestedLevel++; + } + ParseLexedCAttributeList(ProtoLateAttrs, false); + /* TO_UPSTREAM(BoundsSafety) OFF */ } /// Bounds attributes (e.g., counted_by): @@ -3408,7 +3532,8 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form) { + ParsedAttr::Form Form, + unsigned NestedTypeLevel) { // TO_UPSTREAM(BoundsSafety) assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); BalancedDelimiterTracker Parens(*this, tok::l_paren); @@ -3421,6 +3546,10 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, } ArgsVector ArgExprs; + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: Don't diagnose the argument when the attribute is ignored. + // Currently, the argument expressions are always diagnosed even if + /* TO_UPSTREAM(BoundsSafety) OFF */ // Don't evaluate argument when the attribute is ignored. using ExpressionKind = Sema::ExpressionEvaluationContextRecord::ExpressionKind; @@ -3441,9 +3570,12 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, ASTContext &Ctx = Actions.getASTContext(); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Pass NestedTypeLevel ArgExprs.push_back(IntegerLiteral::Create( - Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), 0), + Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), NestedTypeLevel), Ctx.getSizeType(), SourceLocation())); + /* TO_UPSTREAM(BoundsSafety) OFF */ Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()), ScopeName, ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form); @@ -4788,7 +4920,7 @@ void Parser::ParseDeclarationSpecifiers( // type qualifier. diagnoseUseOfC11Keyword(Tok); if (NextToken().is(tok::l_paren)) { - ParseAtomicSpecifier(DS); + ParseAtomicSpecifier(DS, /*TO_UPSTREAM(BoundsSafety)*/LateAttrs); continue; } isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID, @@ -4889,6 +5021,10 @@ void Parser::ParseDeclarationSpecifiers( static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS, Parser &P) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (P.getLangOpts().BoundsSafetyAttributes) + return; + /*TO_UPSTREAM(BoundsSafety) OFF*/ if (DS.getTypeSpecType() != DeclSpec::TST_struct) return; @@ -4957,7 +5093,9 @@ void Parser::ParseStructDeclaration( MaybeParseCXX11Attributes(Attrs); // Parse the common specifier-qualifiers-list piece. - ParseSpecifierQualifierList(DS); + ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateFieldAttrs); // If there are no declarators, this is a free-standing declaration // specifier. Let the actions module cope with it. @@ -5018,8 +5156,11 @@ void Parser::ParseStructDeclaration( // We're done with this declarator; invoke the callback. Decl *Field = FieldsCallback(DeclaratorInfo); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Upstream doesn't pass `DeclaratorInfo.D`. if (Field) - DistributeCLateParsedAttrs(Field, LateFieldAttrs); + DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. @@ -5076,9 +5217,45 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, assert(LA.Decls.size() <= 1 && "late field attribute expects to have at most one declaration."); - // Dispatch based on the attribute and parse it - ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr, - SourceLocation(), ParsedAttr::Form::GNU(), nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + Decl *D = LA.Decls.empty() ? nullptr : LA.Decls[0]; + + // If the Decl is on a function, add function parameters to the scope. + { + // Support for adding function scope is not yet upstream. + std::unique_ptr Scope; + EnterScope &= D && D->isFunctionOrFunctionTemplate(); + if (EnterScope) { + Scope.reset(new ParseScope(this, Scope::FnScope | Scope::DeclScope)); + Actions.ActOnReenterFunctionContext(Actions.CurScope, D); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + + // Dispatch based on the attribute and parse it + /* TO_UPSTREAM(BoundsSafety) ON */ + // NestedTypeLevel is not passed in upstream + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr, + SourceLocation(), ParsedAttr::Form::GNU(), nullptr, + LA.NestedTypeLevel); + /* TO_UPSTREAM(BoundsSafety) OFF */ + + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnterScope) { + Actions.ActOnExitFunctionContext(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + const auto &SM = PP.getSourceManager(); + CharSourceRange ExpansionRange = SM.getExpansionRange(LA.AttrNameLoc); + StringRef FoundName = + Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()) + .split('(') + .first; + IdentifierInfo *MacroII = PP.getIdentifierInfo(FoundName); + for (unsigned i = 0; i < Attrs.size(); ++i) + Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin(), + SM.isInSystemMacro(LA.AttrNameLoc)); for (auto *D : LA.Decls) Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs); @@ -5230,13 +5407,21 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // If attributes exist after struct contents, parse them. MaybeParseGNUAttributes(attrs, &LateFieldAttrs); - // Late parse field attributes if necessary. - ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false); - + /* TO_UPSTREAM(BoundsSafety) ON */ SmallVector FieldDecls(TagDecl->fields()); - Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls, T.getOpenLocation(), T.getCloseLocation(), attrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ + + // Late parse field attributes if necessary. + /* TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().CPlusPlus) { + // FIXME : Properly handle CXX structs + for (auto *LateAttr : LateFieldAttrs) + LateAttr->ParseLexedAttributes(); + } /* TO_UPSTREAM(BoundsSafety) OFF */ else + ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false); + StructScope.Exit(); Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange()); } @@ -6448,7 +6633,9 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, void Parser::ParseTypeQualifierListOpt( DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed, bool IdentifierRequired, - std::optional> CodeCompletionHandler) { + std::optional> CodeCompletionHandler, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { if ((AttrReqs & AR_CXX11AttributesParsed) && isAllowedCXX11AttributeSpecifier()) { ParsedAttributes Attrs(AttrFactory); @@ -6588,7 +6775,14 @@ void Parser::ParseTypeQualifierListOpt( // recovery is graceful. if (AttrReqs & AR_GNUAttributesParsed || AttrReqs & AR_GNUAttributesParsedAndRejected) { - ParseGNUAttributes(DS.getAttributes()); + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + // Upstream doesn't pass LateAttrs + ParseGNUAttributes(DS.getAttributes(), LateAttrs, nullptr); + /*TO_UPSTREAM(BoundsSafety) OFF*/ continue; // do *not* consume the next token! } // otherwise, FALL THROUGH! @@ -6660,6 +6854,16 @@ static bool isPipeDeclarator(const Declarator &D) { return false; } +/* TO_UPSTREAM(BoundsSafety) ON */ +// Late parsing for attributes on the type position is currently only supported +// with -fbounds-safety or -fbounds-safety-attributes. +// Checking `BoundsSafety` is redundant because -fbounds-safety-attributes +// is always implied with -fbounds-safety, but this is added for readability. +static bool enableTypeAttrLateParsing(const LangOptions &LangOpts) { + return LangOpts.BoundsSafety || LangOpts.hasBoundsSafetyAttributes(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// ParseDeclaratorInternal - Parse a C or C++ declarator. The direct-declarator /// is parsed by the function passed to it. Pass null, and the direct-declarator /// isn't parsed at all, making this function effectively parse the C++ @@ -6777,20 +6981,80 @@ void Parser::ParseDeclaratorInternal(Declarator &D, ((D.getContext() != DeclaratorContext::CXXNew) ? AR_GNUAttributesParsed : AR_GNUAttributesParsedAndRejected); - ParseTypeQualifierListOpt(DS, Reqs, true, !D.mayOmitIdentifier()); + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + LateParsedAttrList *LateAttrsPtr = + enableTypeAttrLateParsing(getLangOpts()) ? &LateAttrs : nullptr; + // Upstream doesn't pass LateAttrsPtr + ParseTypeQualifierListOpt(DS, Reqs, true, !D.mayOmitIdentifier(), + std::nullopt, LateAttrsPtr); + /* TO_UPSTREAM(BoundsSafety) OFF */ D.ExtendWithDeclSpec(DS); // Recursively parse the declarator. Actions.runWithSufficientStackSpace( D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); }); - if (Kind == tok::star) + if (Kind == tok::star) { + /* TO_UPSTREAM(BoundsSafety) ON */ + SmallVector LateAttrInfos; + // We parse '__counted_by' or '__ended_by' attributes immediately + // if this is a function declarator. We need these attributes to be + // parsed early so we can construct the full function type before Sema + // is checking and merging the function declaration with the previous + // declaration. + if (getLangOpts().hasBoundsSafetyAttributes() && + getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) { + unsigned FuncIndex = 0; + if (D.isFunctionDeclarator(FuncIndex)) { + // Upstream doesn't support bounds safety attributes on functions yet + // so avoid taking this path when bounds safety is off. + DeclaratorChunk::FunctionTypeInfo FTI = D.getFunctionTypeInfo(); + ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); + for (unsigned i = 0; i != FTI.NumParams; ++i) { + if (Decl *D = FTI.Params[i].Param) { + // Param can be missing if the declaration is malformed. The + // diagnostic is emitted later. + Actions.ActOnReenterCXXMethodParameter( + getCurScope(), cast(D)); + } + } + // Handling nested return type with counted_by, e.g.: + // T *__counted_by(x) *foo(size_t x); + unsigned NestedLevel = 0; + for (unsigned i = FuncIndex + 1; i != D.getNumTypeObjects(); ++i) { + if (D.getTypeObject(i).Kind == DeclaratorChunk::Pointer) + NestedLevel++; + } + + if (NestedLevel != 0) { + for (auto *LateAttr : LateAttrs) { + LateAttr->NestedTypeLevel = NestedLevel; + } + } + ParseLexedCAttributeList(LateAttrs, false, &D.getAttributes()); + } else { + for (auto LA : LateAttrs) { + auto LI = new DeclaratorChunk::LateParsedAttrInfo( + LA->Toks, LA->AttrName, LA->AttrNameLoc); + LateAttrInfos.push_back(LI); + delete LA; + } + } + LateAttrs.clear(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer( DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(), DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(), - DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()), + DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc(), + // TO_UPSTREAM(BoundsSafety) + LateAttrInfos), std::move(DS.getAttributes()), SourceLocation()); - else + } else // Remember that we parsed a Block type, and remember the type-quals. D.AddTypeInfo( DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc), @@ -7574,6 +7838,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D, LParenLoc = Tracker.getOpenLocation(); StartLoc = LParenLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateParamAttrs(/*PSoon=*/false, + /*LateAttrParseExperimentalExtOnly=*/true); + /* TO_UPSTREAM(BoundsSafety) OFF */ + if (isFunctionDeclaratorIdentifierList()) { if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -7591,7 +7860,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc); + ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc, + &LateParamAttrs); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -7693,6 +7963,12 @@ void Parser::ParseFunctionDeclarator(Declarator &D, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + for (auto LateParamAttr : LateParamAttrs) { + ParseLexedCAttribute(*LateParamAttr, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Collect non-parameter declarations from the prototype if this is a function // declaration. They will be moved into the scope of the function. Only do // this in C and not C++, where the decls will continue to live in the @@ -7868,7 +8144,8 @@ void Parser::ParseFunctionDeclaratorIdentifierList( void Parser::ParseParameterDeclarationClause( DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) { + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration, + LateParsedAttrList *LateParamAttrs) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -7947,10 +8224,18 @@ void Parser::ParseParameterDeclarationClause( Diag(ThisLoc, diag::err_requires_expr_explicit_object_parameter); } + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateParamAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateParamAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + ParsedTemplateInfo TemplateInfo; ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DeclSpecContext::DSC_normal, - /*LateAttrs=*/nullptr, AllowImplicitTypename); + // TO_UPSTREAM(BoundsSafety): LateAttrs + LateParamAttrs, AllowImplicitTypename); DS.takeAttributesFrom(ArgDeclSpecAttrs); @@ -7969,7 +8254,13 @@ void Parser::ParseParameterDeclarationClause( ParmDeclarator.SetRangeBegin(ThisLoc); // Parse GNU attributes, if present. - MaybeParseGNUAttributes(ParmDeclarator); + /* TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateParamAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateParamAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + MaybeParseGNUAttributes(ParmDeclarator, LateParamAttrs); if (getLangOpts().HLSL) MaybeParseHLSLAnnotations(DS.getAttributes()); @@ -8039,6 +8330,9 @@ void Parser::ParseParameterDeclarationClause( // added to the current scope. Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator, ThisLoc); + // TO_UPSTREAM(BoundsSafety) + DistributeCLateParsedAttrs(ParmDeclarator, Param, LateParamAttrs); + // Parse the default argument, if any. We parse the default // arguments in all dialects; the semantic analysis in // ActOnParamDefaultArgument will reject the default argument in @@ -8192,7 +8486,15 @@ void Parser::ParseBracketDeclarator(Declarator &D) { // If there is a type-qualifier-list, read it now. // Type qualifiers in an array subscript are a C99 feature. DeclSpec DS(AttrFactory); - ParseTypeQualifierListOpt(DS, AR_CXX11AttributesParsed); + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + LateParsedAttrList *LateAttrsPtr = + enableTypeAttrLateParsing(getLangOpts()) ? &LateAttrs : nullptr; + ParseTypeQualifierListOpt(DS, + AR_CXX11AttributesParsed | AR_GNUAttributesParsed, + true, false, std::nullopt, LateAttrsPtr); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we haven't already read 'static', check to see if there is one after the // type-qualifier-list. @@ -8250,11 +8552,23 @@ void Parser::ParseBracketDeclarator(Declarator &D) { MaybeParseCXX11Attributes(DS.getAttributes()); + /* TO_UPSTREAM(BoundsSafety) ON */ + SmallVector LateAttrInfos; + for (auto LA : LateAttrs) { + auto LI = new DeclaratorChunk::LateParsedAttrInfo( + LA->Toks, LA->AttrName, LA->AttrNameLoc); + LateAttrInfos.push_back(LI); + delete LA; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Remember that we parsed a array type, and remember its features. D.AddTypeInfo( DeclaratorChunk::getArray(DS.getTypeQualifiers(), StaticLoc.isValid(), isStar, NumElements.get(), T.getOpenLocation(), - T.getCloseLocation()), + T.getCloseLocation(), + // TO_UPSTREAM(BoundsSafety) + LateAttrInfos), std::move(DS.getAttributes()), T.getCloseLocation()); } @@ -8314,10 +8628,15 @@ void Parser::ParseMisplacedBracketDeclarator(Declarator &D) { } // Adding back the bracket info to the end of the Declarator. - for (unsigned i = 0, e = TempDeclarator.getNumTypeObjects(); i < e; ++i) { - const DeclaratorChunk &Chunk = TempDeclarator.getTypeObject(i); - D.AddTypeInfo(Chunk, TempDeclarator.getAttributePool(), SourceLocation()); - } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) + D.TakeTypeObjects(TempDeclarator); + else + /* TO_UPSTREAM(BoundsSafety) OFF*/ + for (unsigned i = 0, e = TempDeclarator.getNumTypeObjects(); i < e; ++i) { + const DeclaratorChunk &Chunk = TempDeclarator.getTypeObject(i); + D.AddTypeInfo(Chunk, TempDeclarator.getAttributePool(), SourceLocation()); + } // The missing identifier would have been diagnosed in ParseDirectDeclarator. // If parentheses are required, always suggest them. @@ -8435,7 +8754,8 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) { /// [C11] atomic-specifier: /// _Atomic ( type-name ) /// -void Parser::ParseAtomicSpecifier(DeclSpec &DS) { +// TO_UPSTREAM(BoundsSafety): LateAttrs +void Parser::ParseAtomicSpecifier(DeclSpec &DS, LateParsedAttrList *LateAttrs) { assert(Tok.is(tok::kw__Atomic) && NextToken().is(tok::l_paren) && "Not an atomic specifier"); @@ -8444,7 +8764,11 @@ void Parser::ParseAtomicSpecifier(DeclSpec &DS) { if (T.consumeOpen()) return; - TypeResult Result = ParseTypeName(); + TypeResult Result = + ParseTypeName(/*Range=*/nullptr, DeclaratorContext::TypeName, AS_none, + /*OwnedType=*/nullptr, /*Attrs=*/nullptr, + // TO_UPSTREAM(BoundsSafety) + LateAttrs); if (Result.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return; @@ -8617,6 +8941,90 @@ TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context, return Result; } +/* TO_UPSTREAM(BoundsSafety) ON */ +ExprResult +Parser::ParseBoundsAttributeArgFromString(StringRef ExprStr, StringRef Context, + Decl *ParentDecl, + SourceLocation IncludeLoc) { + // Consume (unexpanded) tokens up to the end-of-directive. + SmallVector Tokens; + { + // Create a new buffer from which we will parse the type. + auto &SourceMgr = PP.getSourceManager(); + FileID FID = SourceMgr.createFileID( + llvm::MemoryBuffer::getMemBufferCopy(ExprStr, Context), SrcMgr::C_User, + 0, 0, IncludeLoc); + + // Form a new lexer that references the buffer. + Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP); + L.setParsingPreprocessorDirective(true); + L.setIsPragmaLexer(true); + + // Lex the tokens from that buffer. + Token Tok; + do { + L.Lex(Tok); + Tokens.push_back(Tok); + } while (Tok.isNot(tok::eod)); + } + + // Replace the "eod" token with an "eof" token identifying the end of + // the provided string. + Token &EndToken = Tokens.back(); + EndToken.startToken(); + EndToken.setKind(tok::eof); + EndToken.setLocation(Tok.getLocation()); + EndToken.setEofData(ExprStr.data()); + + // Add the current token back. + Tokens.push_back(Tok); + + // Enter the tokens into the token stream. + PP.EnterTokenStream(Tokens, /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); + + // Consume the current token so that we'll start parsing the tokens we + // added to the stream. + ConsumeAnyToken(); + + // Enter a new scope. + std::unique_ptr LocalScope; + bool EnterScope = ParentDecl && ParentDecl->isFunctionOrFunctionTemplate(); + if (EnterScope) { + LocalScope.reset(new ParseScope(this, Scope::FnScope | Scope::DeclScope)); + Actions.ActOnReenterFunctionContext(Actions.CurScope, ParentDecl); + } + + // Parse the expr. + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr, + ExpressionKind::EK_AttrArgument); + + ExprResult Result( + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); + + // Check if we parsed the whole thing. + if (Result.isUsable() && + (Tok.isNot(tok::eof) || Tok.getEofData() != ExprStr.data())) { + Diag(Tok.getLocation(), diag::err_type_unparsed); + } + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the 'end of directive' token. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the end token. + if (Tok.is(tok::eof) && Tok.getEofData() == ExprStr.data()) + ConsumeAnyToken(); + if (EnterScope) + Actions.ActOnExitFunctionContext(); + return Result; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void Parser::DiagnoseBitIntUse(const Token &Tok) { // If the token is for _ExtInt, diagnose it as being deprecated. Otherwise, // the token is about _BitInt and gets (potentially) diagnosed as use of an diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 59db972ff443e..f44e630c72290 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3323,6 +3323,14 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) LateParsedAttrs[i]->addDecl(ThisDecl); + + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateCAttrs; + + DistributeCLateParsedAttrs(DeclaratorInfo, ThisDecl, &LateCAttrs); + for (auto *LCA : LateCAttrs) + getCurrentClass().LateParsedDeclarations.push_back(LCA); + /* TO_UPSTREAM(BoundsSafety) OFF */ } Actions.FinalizeDeclaration(ThisDecl); DeclsInGroup.push_back(ThisDecl); @@ -3631,7 +3639,8 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclarationWithPragmas( return nullptr; } ParsedTemplateInfo TemplateInfo; - return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo); + return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo, + /*TemplateDiags=*/nullptr); } } @@ -3852,7 +3861,6 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, Actions.ActOnFinishCXXMemberSpecification(getCurScope(), RecordLoc, TagDecl, T.getOpenLocation(), T.getCloseLocation(), attrs); - // C++11 [class.mem]p2: // Within the class member-specification, the class is regarded as complete // within function bodies, default arguments, exception-specifications, and diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index e82b565272831..30fe6dc1b8cd1 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1566,6 +1566,44 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; Res = ParseBuiltinBitCast(); break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case tok::kw___builtin_unsafe_forge_bidi_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeBidiIndexable(); + break; + case tok::kw___builtin_unsafe_forge_single: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeSingle(); + break; + case tok::kw___builtin_unsafe_forge_terminated_by: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeTerminatedBy(); + break; + case tok::kw___builtin_get_pointer_lower_bound: + case tok::kw___builtin_get_pointer_upper_bound: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseGetPointerBound( + SavedKind == tok::kw___builtin_get_pointer_lower_bound + ? PBK_Lower : PBK_Upper); + break; + case tok::kw___builtin_terminated_by_to_indexable: + case tok::kw___builtin_unsafe_terminated_by_to_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseTerminatedByToIndexable( + /*Unsafe=*/SavedKind == + tok::kw___builtin_unsafe_terminated_by_to_indexable); + break; + case tok::kw___builtin_unsafe_terminated_by_from_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeTerminatedByFromIndexable(); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ case tok::kw_typeid: if (NotPrimaryExpression) *NotPrimaryExpression = true; @@ -4002,3 +4040,188 @@ ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) { return Actions.ObjC().ActOnObjCAvailabilityCheckExpr( AvailSpecs, BeginLoc, Parens.getCloseLocation()); } + +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// BoundsSafety: Parse __builtin_unsafe_forge_bidi_indexable(ptr, size). +ExprResult Parser::ParseUnsafeForgeBidiIndexable() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_unsafe_forge_bidi_indexable")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Size = ParseAssignmentExpression(); + if (Size.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeBidiIndexable(KWLoc, Addr.get(), Size.get(), + T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_single(ptr). +ExprResult Parser::ParseUnsafeForgeSingle() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_unsafe_forge_single")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeSingle(KWLoc, Addr.get(), T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_forge_terminated_by(ptr, terminator). +ExprResult Parser::ParseUnsafeForgeTerminatedBy() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unsafe_forge_terminated_by")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Term = ParseConstantExpression(); + if (Term.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeTerminatedBy(KWLoc, Addr.get(), Term.get(), + T.getCloseLocation()); +} + +ExprResult Parser::ParseGetPointerBound(PointerBoundKind K) { + SourceLocation KWLoc = ConsumeToken(); + ExprResult (Sema::*ActOnBoundExpr)(Expr *, SourceLocation, SourceLocation); + const char *ExpectName; + + if (K == PBK_Lower) { + ExpectName = "__builtin_get_pointer_lower_bound"; + ActOnBoundExpr = &Sema::ActOnGetLowerBound; + } else { + ExpectName = "__builtin_get_pointer_upper_bound"; + ActOnBoundExpr = &Sema::ActOnGetUpperBound; + } + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, ExpectName)) + return ExprError(); + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return (Actions.*ActOnBoundExpr)(Pointer.get(), KWLoc, T.getCloseLocation()); +} + +/// BoundsSafety: +/// __builtin_terminated_by_to_indexable(pointer [, terminator]) +/// __builtin_unsafe_terminated_by_to_indexable(pointer [, terminator]) +ExprResult Parser::ParseTerminatedByToIndexable(bool Unsafe) { + SourceLocation KWLoc = ConsumeToken(); + + const char *ExpectName; + ExprResult (Sema::*Act)(Expr * PointerExpr, Expr * TerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc); + if (Unsafe) { + ExpectName = "__builtin_unsafe_terminated_by_to_indexable"; + Act = &Sema::ActOnUnsafeTerminatedByToIndexable; + } else { + ExpectName = "__builtin_terminated_by_to_indexable"; + Act = &Sema::ActOnTerminatedByToIndexable; + } + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, ExpectName)) + return ExprError(); + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *Terminator = nullptr; + if (TryConsumeToken(tok::comma)) { + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid()) + return ExprError(); + Terminator = Res.get(); + } + + if (T.consumeClose()) + return ExprError(); + + return (Actions.*Act)(Pointer.get(), Terminator, KWLoc, T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_terminated_by_from_indexable(terminator, +/// pointer [, pointer-to-terminator]). +ExprResult Parser::ParseUnsafeTerminatedByFromIndexable() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unsafe_terminated_by_from_indexable")) + return ExprError(); + + ExprResult Terminator = ParseAssignmentExpression(); + if (Terminator.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *PointerToTerminator = nullptr; + if (TryConsumeToken(tok::comma)) { + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid()) + return ExprError(); + PointerToTerminator = Res.get(); + } + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnUnsafeTerminatedByFromIndexable( + Terminator.get(), Pointer.get(), PointerToTerminator, KWLoc, + T.getCloseLocation()); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 45929e6a48d8a..86210ab7bac2b 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -1455,7 +1455,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, SmallVector KeyIdents; SmallVector KeyLocs; - SmallVector ObjCParamInfo; + SmallVector ArgInfos; ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); @@ -1496,9 +1496,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ArgInfo.NameLoc = Tok.getLocation(); ConsumeToken(); // Eat the identifier. - ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration( - getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition); - ObjCParamInfo.push_back(Param); + ArgInfos.push_back(ArgInfo); KeyIdents.push_back(SelIdent); KeyLocs.push_back(selLoc); @@ -1558,6 +1556,17 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, nullptr)); } + // Turn ArgInfos into parameters. This must happen after parsing all + // parameters for bug compatibility with previous versions of Clang. (For + // instance, if a method declares a parameter called "id", that parameter must + // not shadow the "id" type.) + SmallVector ObjCParamInfo; + for (auto &ArgInfo : ArgInfos) { + ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration( + getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition); + ObjCParamInfo.push_back(Param); + } + // FIXME: Add support for optional parameter list... // If attributes exist after the method, parse them. MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0), diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 6a5a219f183eb..31e6dd3361cc1 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -76,6 +76,14 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) [this](StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc) { return this->ParseTypeFromString(TypeStr, Context, IncludeLoc); }; + /* TO_UPSTREAM(BoundsSafety) ON */ + Actions.ParseBoundsAttributeArgFromStringCallback = + [this](StringRef ExprStr, StringRef Context, Decl *Parent, + SourceLocation IncludeLoc) { + return this->ParseBoundsAttributeArgFromString(ExprStr, Context, Parent, + IncludeLoc); + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ } DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { @@ -468,8 +476,9 @@ Parser::ParseScopeFlags::~ParseScopeFlags() { //===----------------------------------------------------------------------===// Parser::~Parser() { - // Clear out the parse-type-from-string callback. + // Clear out the parse-*-from-string callbacks. Actions.ParseTypeFromStringCallback = nullptr; + Actions.ParseBoundsAttributeArgFromStringCallback = nullptr; // If we still have scopes active, delete the scope tree. delete getCurScope(); @@ -1154,8 +1163,13 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( ParsedTemplateInfo TemplateInfo; MaybeParseMicrosoftAttributes(DS.getAttributes()); // Parse the common declaration-specifiers piece. + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList BoundsSafetyLateAttrs( + /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, - DeclSpecContext::DSC_top_level); + DeclSpecContext::DSC_top_level, + &BoundsSafetyLateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. @@ -1250,7 +1264,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, DeclaratorContext::File, Attrs, TemplateInfo); + return ParseDeclGroup(DS, DeclaratorContext::File, Attrs, TemplateInfo, + /*DeclEnd=*/nullptr, + /*FRI=*/nullptr, + // TO_UPSTREAM(BoundsSafety) + &BoundsSafetyLateAttrs); } Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( @@ -1292,7 +1310,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( /// Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo, - LateParsedAttrList *LateParsedAttrs) { + LateParsedAttrList *LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs) { llvm::TimeTraceScope TimeScope("ParseFunctionDefinition", [&]() { return Actions.GetNameForDeclarator(D).getName().getAsString(); }); @@ -1522,7 +1542,16 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, } else Actions.ActOnDefaultCtorInitializers(Res); + /*TO_UPSTREAM(BoundsSafety) ON*/ // Late attributes are parsed in the same scope as the function body. + LateParsedAttrList BoundsSafetyAttrList(true); + if (!BoundsSafetyLateAttrs) + BoundsSafetyLateAttrs = &BoundsSafetyAttrList; + DistributeCLateParsedAttrs(D, Res, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, false); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LateParsedAttrs) ParseLexedAttributeList(*LateParsedAttrs, Res, false, true); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index c76733e9a774b..a5cf729d9f84e 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -13,6 +13,8 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/AnalysisBasedWarnings.h" +// TO_UPSTREAM(BoundsSafety) +#include "clang/Sema/BoundsSafetySuggestions.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -2347,6 +2349,44 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { } } + /* TO_UPSTREAM(BoundsSafety) ON */ + void handleUnsafeCountAttributedPointerArgument(const CallExpr *Call, + const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) override { + const FunctionDecl *FD = Call->getDirectCallee(); + const auto *FPT = FD->getType()->getAs(); + + const auto ArgIt = std::find(Call->arg_begin(), Call->arg_end(), Arg); + assert(ArgIt != Call->arg_end()); + const unsigned ArgIndex = ArgIt - Call->arg_begin(); + + const auto *CATy = + FPT->getParamType(ArgIndex)->getAs(); + + // For __counted_by(C) where C is just a DRE, we recommend passing '.data()' + // to the pointer and '.size()' to the count parameter. For other C, don't + // recommend it. + bool IsSimpleCount = + CATy->getNumCoupledDecls() == 1 && + isa(CATy->getCountExpr()->IgnoreParenImpCasts()); + + const ParmVarDecl *PVD = FD->getParamDecl(ArgIndex); + + StringRef PtrParamName = PVD->getName(); + StringRef CountParamName = + IsSimpleCount ? CATy->dependent_decls().begin()->getDecl()->getName() + : ""; + + S.Diag(Arg->getBeginLoc(), + diag::warn_unsafe_count_attributed_pointer_argument); + S.Diag(PVD->getBeginLoc(), + diag::note_unsafe_count_attributed_pointer_argument) + << IsSimpleCount << QualType(CATy, 0) << !PtrParamName.empty() + << PtrParamName << CountParamName; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, @@ -2436,6 +2476,577 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { }; } // namespace +/* TO_UPSTREAM(BoundsSafety) ON*/ +//===----------------------------------------------------------------------===// +// -fbounds-safety suggestions. +//===----------------------------------------------------------------------===// + +class BoundsSafetySuggestionReporter : public BoundsSafetySuggestionHandler { +public: + BoundsSafetySuggestionReporter(Sema &S) : S(S) {} + +private: + Sema &S; + + int UnsafeOpToSelectIndex(UnsafeOpKind Kind) { + // Don't trust the enum to stay in sync with our %select (layering). + switch (Kind) { + case UnsafeOpKind::Index: + return 0; + case UnsafeOpKind::Arithmetic: + return 1; + case UnsafeOpKind::Deref: + return 2; + case UnsafeOpKind::MemberAccess: + return 3; + case UnsafeOpKind::Assignment: + return 4; + case UnsafeOpKind::Return: + return 5; + case UnsafeOpKind::CallArg: + return 6; + case UnsafeOpKind::Cast: + return 7; + } + llvm_unreachable("Unhandled UnsafeOpKind"); + } + + int WillTrapKindSelectIndex(WillTrapKind Kind) { + switch (Kind) { + case WillTrapKind::NoTrap: + return 0; + case WillTrapKind::Unknown: + return 1; + case WillTrapKind::Trap: + return 2; + case WillTrapKind::TrapIffPtrNotNull: + return 3; + case WillTrapKind::TrapIffPtrNull: + return 4; + } + llvm_unreachable("Unhandled WillTrapKind"); + } + + int PtrArithOOBKindSelectIndex(PtrArithOOBKind Kind) { + switch (Kind) { + case PtrArithOOBKind::ALWAYS_OOB_BASE_OOB: + return 0; + case PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET: + return 1; + case PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET: + return 2; + case PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO: + return 3; + case PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO: + return 4; + case PtrArithOOBKind::UNKNOWN: + case PtrArithOOBKind::NEVER_OOB: + llvm_unreachable("Should not emit diagnostic with this kind"); + } + llvm_unreachable("Unhandled PtrArithOOBKind"); + } + + int AssignmentExprToSelectIndex(const Expr *AssignmentExpr) { + if (AssignmentExpr) { + return 0; // "assigned" + } + return 1; // "initialized" + } + + void emitSingleEntityDeclaredHereNote(const SingleEntity Entity) { + switch (Entity.Kind) { + case AssignmentSourceKind::Parameter: + case AssignmentSourceKind::GlobalVar: + case AssignmentSourceKind::LocalVar: + S.Diag(Entity.Entity->getBeginLoc(), diag::note_pointer_declared_here) + << Entity.Entity; + break; + case AssignmentSourceKind::FunctionCallReturnValue: + case AssignmentSourceKind::ArrayElement: + S.Diag(Entity.Entity->getBeginLoc(), diag::note_entity_declared_at) + << Entity.Entity; + break; + case AssignmentSourceKind::StructMember: + case AssignmentSourceKind::UnionMember: + // Use qualified name so diagnostic consumer knowns which struct the + // member came from. + S.Diag(Entity.Entity->getBeginLoc(), diag::note_entity_declared_at) + << Entity.Entity->getQualifiedNameAsString(); + break; + } + } + + void EmitSingleEntityAssignedToPointerHereNote( + const SingleEntity &Entity, const VarDecl *IndexableLocalVar, + const Stmt *UnsafeOp, UnsafeOpKind Kind, const Expr *AssignmentExpr, + const QualType UnsafeOpPointerTy, bool IdentifyEntityKind) { + + bool IsInit = AssignmentExpr == IndexableLocalVar->getInit(); + + // Check if the pointee type sizes match. If they don't we'll want to emit + // a different diagnostic that notes the type sizes. + const auto UnsafeOpPointeeTy = UnsafeOpPointerTy->getPointeeType(); + const auto IndexableLocalVarPointeeTy = + IndexableLocalVar->getType()->getAs()->getPointeeType(); + + bool PointeeTypesSizesMatch = + S.getASTContext().getTypeSizeOrNull(IndexableLocalVarPointeeTy) == + S.getASTContext().getTypeSizeOrNull(UnsafeOpPointeeTy); + + if (PointeeTypesSizesMatch) { + if (IdentifyEntityKind) { + // TODO(dliew): We should make a distinction between initialization + // and assignment here (rdar://122940682). + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here) + << Entity.Kind << Entity.Entity << IndexableLocalVar + << AssignmentExpr->getSourceRange(); + return; + } + S.Diag(AssignmentExpr->getBeginLoc(), + IsInit ? diag::note_pointer_initialized_here + : diag::note_pointer_assigned_here) + << IndexableLocalVar; + return; + } + + // The Pointee sizes don't match. Use a diagnostic that explicitly states + // the size of the different pointee types in bytes. + // The diagnostic text will mention the type of the cast. We make the + // assumption that if the pointee sizes are different that there must be a + // cast somewhere. + int IsInitSelect = IsInit ? 1 : 0; + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ UnsafeOpPointeeTy << /*6*/ S.getASTContext() + .getTypeSizeInChars(UnsafeOpPointeeTy) + .getQuantity() + << /*7*/ 1 /*Cast of IndexableLocalVar has UnsafeOpPointerTy type*/ + << /*8*/ UnsafeOpPointerTy << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + } + + void handleSingleEntityFlowingToIndexableVariable( + const SingleEntity Entity, const VarDecl *IndexableLocalVar, + const Stmt *UnsafeOp, UnsafeOpKind Kind, const QualType UnsafeOpPtrTy, + PtrArithOOBKind PtrArithIsOOB = PtrArithOOBKind::UNKNOWN, + size_t MinimumPtrArithOOBOffset = 0) { + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int OpSelect = UnsafeOpToSelectIndex(Kind); + int IsInitialization = AssignmentExprToSelectIndex(AssignmentExpr); + int PtrArithOOBSelect = PtrArithOOBKindSelectIndex(PtrArithIsOOB); + + if (Kind == UnsafeOpKind::Index && + PtrArithIsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // This case is handled by + // `handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB` + llvm_unreachable("This method should not handle this case"); + } + + S.Diag(UnsafeOp->getBeginLoc(), + diag::warn_bounds_safety_conv_single_to_implicit_indexable) + << Entity.Kind << OpSelect << IndexableLocalVar << IsInitialization + << Entity.Entity << PtrArithOOBSelect << MinimumPtrArithOOBOffset + << UnsafeOp->getSourceRange(); + + if (AssignmentExpr) { + // Pointer is assigned to. Report the assignment location and where + // the pointer is declared. + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, AssignmentExpr, + UnsafeOpPtrTy, /*IdentifyEntityKind=*/false); + + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + } else { + // The pointer was initialized. + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, + IndexableLocalVar->getInit(), UnsafeOpPtrTy, + /*IdentifyEntityKind=*/false); + + // Don't report diag::note_pointer_declared_here because the + // initialization and declaration location will be very similar + } + + emitSingleEntityDeclaredHereNote(Entity); + } + + void handleSingleEntitiesFlowingToIndexableVariableImpl( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const QualType UnsafeOpPtrTy, + PtrArithOOBKind PtrArithIsOOB = PtrArithOOBKind::UNKNOWN, + size_t MinimumPtrArithOOBOffset = 0) { + int OpSelect = UnsafeOpToSelectIndex(Kind); + int PtrArithOOBSelect = PtrArithOOBKindSelectIndex(PtrArithIsOOB); + + if (Kind == UnsafeOpKind::Index && + PtrArithIsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // This case is handled by + // `handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB` + llvm_unreachable("This method should not handle this case"); + } + + S.Diag( + UnsafeOp->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_multiple_assignments) + << OpSelect << IndexableLocalVar << PtrArithOOBSelect + << MinimumPtrArithOOBOffset << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + } + assert(AssignmentExpr); + + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, AssignmentExpr, + UnsafeOpPtrTy, /*IdentifyEntityKind=*/true); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + bool IsReallySinglePtr(const QualType Ty) const { + if (!Ty->isSinglePointerType()) + return false; + + // Unfortunately __counted_by and friends, and __terminated_by use sugar + // types wrapping a __single so we need to check for those explicitly and + // bail in those cases. + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; + + return true; + }; + +public: + void handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + PtrArithOOBKind IsOOB, size_t MinimumPtrArithOOBOffset) override { + assert(Entities.size() > 0); + + QualType UnsafeOpPtrTy; + // This method handles both indexing and pointer arithmetic + switch (Kind) { + case UnsafeOpKind::Index: + UnsafeOpPtrTy = cast(UnsafeOp)->getBase()->getType(); + break; + case UnsafeOpKind::Arithmetic: + UnsafeOpPtrTy = cast(UnsafeOp)->getType(); + break; + default: + llvm_unreachable("Unhandled UnsafeOpKind"); + } + + if (Entities.size() == 1) { + // When there is only one __single entity reaching the __bidi_indexable + // variable produce a specialized diagnostic + handleSingleEntityFlowingToIndexableVariable( + Entities[0], IndexableLocalVar, UnsafeOp, Kind, UnsafeOpPtrTy, IsOOB, + MinimumPtrArithOOBOffset); + } else + handleSingleEntitiesFlowingToIndexableVariableImpl( + Entities, IndexableLocalVar, UnsafeOp, Kind, UnsafeOpPtrTy, IsOOB, + MinimumPtrArithOOBOffset); + } + + void handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, + const Expr *Operand, UnsafeOpKind Kind) override { + int OpSelect = UnsafeOpToSelectIndex(Kind); + + auto RealPointeeTy = + Operand->getType()->getAs()->getPointeeType(); + assert(!RealPointeeTy->isIncompleteType()); + // This is rather subtle but we **also** have to determine the pointee type + // with implicit casts stripped. This is because when `Operand` is printed + // it may contain implicit casts which do not show up when printing the + // expression. This could cause very confusing diagnostics that claim the + // the type of an expression is different to what the user would deduce by + // reading the printed expression. To avoid this we determine the + // `PerceivedPointeeTy` which is the type the user would expect from the + // printed form of `Operand`. + const auto *OperandNoParenImpCasts = Operand->IgnoreParenImpCasts(); + assert(OperandNoParenImpCasts->getType()->isPointerType()); + auto PerceivedPointeeTy = OperandNoParenImpCasts->getType() + ->getAs() + ->getPointeeType(); + assert(!PerceivedPointeeTy->isIncompleteType()); + + std::string FieldName(""); + if (Kind == UnsafeOpKind::MemberAccess) { + auto *ME = cast(UnsafeOp); + const auto *FD = cast(ME->getMemberDecl()); + if (FD->getType()->isIncompleteArrayType() && + !FD->getType()->isCountAttributedType()) { + // Suppress the warning for incomplete array types because the + // warn_bounds_safety_promoting_incomplete_array_without_count warning + // already handles this. E.g.: + // + // ``` + // struct Foo { + // int member; + // int* buffer[]; // Size of array is "incomplete" + // }; + // ``` + return; + } + FieldName = FD->getQualifiedNameAsString(); + } + + auto UnsafeOpLoc = UnsafeOp->getBeginLoc(); + if (const auto* ILE = dyn_cast(UnsafeOp)) { + // Initializer lists don't have a location for assignment in + // the list so use `Operand` which is the expression used for + // the assignment instead. + assert(std::any_of( + ILE->begin(), ILE->end(), + [&Operand](const Stmt *InitStmt) { return InitStmt == Operand; })); + UnsafeOpLoc = Operand->getBeginLoc(); + } + + const auto RealPointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(RealPointeeTy).getQuantity(); + const auto PerceivedPointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(PerceivedPointeeTy).getQuantity(); + + S.Diag( + UnsafeOpLoc, + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_unsafe_zeroth_element) + << /*0*/ OpSelect << /*1*/ const_cast(Operand) + << /*2*/ RealPointeeTy << /*3*/ RealPointeeTySizeInBytes + << /*4*/ IndexableLocalVar << /*5*/ FieldName + << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int IsInitSelect = 0; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + IsInitSelect = 1; + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ PerceivedPointeeTy << /*6*/ PerceivedPointeeTySizeInBytes + << /*7*/ 0 /* Operand has element type*/ + << /*8*/ const_cast(Operand) << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + + void handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand) override { + + const auto OperandTy = Operand->getType(); + assert(OperandTy->isPointerTypeWithBounds() || + IsReallySinglePtr(OperandTy)); + + assert(Kind == UnsafeOpKind::Cast); + const auto *CE = cast(UnsafeOp); + + assert(CE->getSubExpr()->getType()->isPointerTypeWithBounds()); + auto CastPointeeTy = CE->getType()->getAs()->getPointeeType(); + + if (CE->getType()->isPointerTypeWithBounds() && + CE->getCastKind() == clang::CK_BitCast) { + // Unsafe bitcast on __bidi_indexable/__indexable + S.Diag( + CE->getBeginLoc(), + diag::warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast) + << ((isa(CE)) ? 1 : 0) << IndexableLocalVar + << CE->getSourceRange(); + } else if (IsReallySinglePtr(CE->getType()) && + CE->getCastKind() == clang::CK_BoundsSafetyPointerCast) { + // __bidi_indexable/__indexable -> __single that traps + S.Diag( + CE->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast_to_single_trap) + << ((isa(CE)) ? 1 : 0) << IndexableLocalVar + << CastPointeeTy << CE->getSourceRange(); + } else { + llvm_unreachable("Unhandled cast type"); + } + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int IsInitSelect = 0; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + IsInitSelect = 1; + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ CastPointeeTy << /*6*/ S.getASTContext() + .getTypeSizeInChars(CastPointeeTy) + .getQuantity() + << /*7*/ 1 /*Cast of IndexableLocalVar has UnsafeBitCast type*/ + << /*8*/ CE->getType() << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + + void handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand, const QualType DCPT, WillTrapKind WillTrap, + std::optional ConstantCount, + size_t MaxSafeSizeOrCount) override { + const auto OperandTy = Operand->getType(); + assert(OperandTy->isPointerTypeWithBounds() || + IsReallySinglePtr(OperandTy)); + + assert(Kind == UnsafeOpKind::Cast || Kind == UnsafeOpKind::CallArg || + Kind == UnsafeOpKind::Return || Kind == UnsafeOpKind::Assignment); + const auto *CE = cast(UnsafeOp); + + assert(CE->getType()->isCountAttributedType()); + + int OpSelect = UnsafeOpToSelectIndex(Kind); + int WillTrapSelect = WillTrapKindSelectIndex(WillTrap); + assert(DCPT->isCountAttributedType()); + int OrNullSelect = + DCPT->getAs()->isOrNull() ? 1 : 0; + int SizedInBytesSelect = + DCPT->getAs()->isCountInBytes() ? 1 : 0; + + auto DCPTPointeeTy = DCPT->getAs()->getPointeeType(); + + int ConstantCountSelect = ConstantCount ? 1 : 0; + size_t ConstantCountInBytes = 0; + if (ConstantCount) { + if (DCPT->getAs()->isCountInBytes()) { + // __sized_by/__sized_by_or_null + ConstantCountInBytes = ConstantCount->getZExtValue(); + } else { + // __counted_by/__counted_by_or_null + auto PointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(DCPTPointeeTy).getQuantity(); + auto PointeeTySizeInBytesAP = + llvm::APInt(ConstantCount->getBitWidth(), PointeeTySizeInBytes); + auto ConstantCountInBytesAP = + ((*ConstantCount) * PointeeTySizeInBytesAP); + ConstantCountInBytes = ConstantCountInBytesAP.getZExtValue(); + } + } + + // Due to a bug (rdar://83900556) checks are not emitted when returning + // a __counted_by(or_null)/__sized_by(or_null) pointer. + int TrapInFutureCompilerVersion = (Kind == UnsafeOpKind::Return) ? 1 : 0; + + S.Diag( + UnsafeOp->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_dynamic_count_conversion) + << /*0*/ OpSelect << /*1*/ IndexableLocalVar << /*2*/ WillTrapSelect + << /*3*/ DCPT << /*4*/ ConstantCountSelect << /*5*/ OrNullSelect + << /*6*/ TrapInFutureCompilerVersion << /*7*/ SizedInBytesSelect + << /*8*/ MaxSafeSizeOrCount << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_dyn_count_conversion) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ ConstantCountSelect << /*6*/ DCPT + << /*7*/ ConstantCountInBytes << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + //===----------------------------------------------------------------------===// // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based // warnings on a function, method, or block. @@ -2824,6 +3435,15 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD)) checkThrowInNonThrowingFunc(S, FD, AC); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !Diags.isIgnored(diag::warn_bounds_safety_conv_single_to_implicit_indexable, + D->getBeginLoc())) { + BoundsSafetySuggestionReporter Reporter(S); + checkBoundsSafetySuggestions(D, Reporter, S); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // If none of the previous checks caused a CFG build, trigger one here // for the logical error handler. if (LogicalErrorHandler::hasActiveDiagnostics(Diags, D->getBeginLoc())) { diff --git a/clang/lib/Sema/BoundsSafetySuggestions.cpp b/clang/lib/Sema/BoundsSafetySuggestions.cpp new file mode 100644 index 0000000000000..ab4240a9ca283 --- /dev/null +++ b/clang/lib/Sema/BoundsSafetySuggestions.cpp @@ -0,0 +1,1800 @@ +//===- BoundsSafetySuggestions.cpp - Improve your -fbounds-safety code ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +// TO_UPSTREAM(BoundsSafety) +#include "clang/Sema/BoundsSafetySuggestions.h" +#include "clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// A map from variables to values stored in them (their potential +// "definitions", as in "reaching definitions" or "use-def chains"). +using DefinitionList = SmallVector; +using DefinitionMap = DenseMap; + +// A visitor that recursively scans an AST subtree to identify values of +// encountered local variables. It's purely syntactic; it doesn't account +// for "happens-before" relationships between definitions, and the answer +// doesn't depend on the point in the program in which possible definitions +// are queried. +// +// Still, it is very useful for identifying values of variables +// in situations when the value is actually unconditional, +// but otherwise unobvious from the AST. Or confirming that +// all possible values fall into a certain category. +// +// The visitor performs exactly one pass over the AST, which is fast enough +// for the compiler. +// +// TODO: Teach the DefinitionVisitor to understand happens-before +// (rdar://117166345). +class DefinitionVisitor : public ConstStmtVisitor { // CRTP! + void VisitChildren(const Stmt *S) { + for (const Stmt *ChildS : S->children()) + if (ChildS) + Visit(ChildS); + } + + bool isSupportedVariable(const Decl *D) { + // We currently support local variables. + if (const auto *VD = dyn_cast_or_null(D)) + if (VD->isLocalVarDecl()) + return true; + + return false; + } + +public: + DefinitionMap DefMap; + + void VisitStmt(const Stmt *S) { + // This is a manual implementation of RecursiveASTVIsitor behavior. + // It only applies to statements and gives us fine control + // over what exactly do we recurse into. + VisitChildren(S); + } + + // These statements are sources of variable values. + void VisitDeclStmt(const DeclStmt *DS); + void VisitBinaryOperator(const BinaryOperator *BO); + + // Unevaluated context visitors + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *) { + // Unevaluated context such as sizeof()/alignof()/__alignof + return; + } + + void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE) { + // Only the result expr is evaluated in `_Generic`. + // E.g. + // _Generic(0, // Not evaluated + // int: foo(), // Evaluated + // char: bar() // Not evaluated + // ); + Visit(GSE->getResultExpr()); + } +}; + +} // namespace + +void DefinitionVisitor::VisitDeclStmt(const DeclStmt *DS) { + for (const Decl *D : DS->decls()) + if (const auto *VD = dyn_cast(D)) + if (const Expr *E = VD->getInit()) { + // An initialization is a definition. An uninitialized variable + // declaration isn't a definition. + if (isSupportedVariable(VD)) + DefMap[VD].push_back(E); + + // The initializer may have more interesting sub-expressions. + // FIXME: For non-variable declarations, should we visit children + // in a different way? VisitChildren() is probably unhelpful + // because children aren't statements(?). + Visit(E); + } +} + +void DefinitionVisitor::VisitBinaryOperator(const BinaryOperator *BO) { + // Compound assignment operations (+= etc.) don't count as definitions. + // They just reuse whatever's already there. + if (BO->getOpcode() == BO_Assign) { + if (const auto *LHSDRE = + dyn_cast(BO->getLHS()->IgnoreParens())) { + const auto *VD = dyn_cast(LHSDRE->getDecl()); + + // An assignment to a variable is a definition of that variable. + if (isSupportedVariable(VD)) + DefMap[VD].push_back(BO->getRHS()); + } + } + + // Continue visitation normally. + VisitChildren(BO); +} + +namespace { + +// The visitor that enumerates unsafe buffer operations and informs the Handler +// about problems associated with them. +// +// TODO: Teach UnsafeOperationVisitor to understand happens-before +// (rdar://117166345). +class UnsafeOperationVisitor : public ConstStmtVisitor { + using UnsafeOpKind = BoundsSafetySuggestionHandler::UnsafeOpKind; + using WillTrapKind = BoundsSafetySuggestionHandler::WillTrapKind; + using PtrArithOOBKind = BoundsSafetySuggestionHandler::PtrArithOOBKind; + + BoundsSafetySuggestionHandler &Handler; + const DefinitionMap &DefMap; + Sema &S; + ASTContext &Ctx; + llvm::SmallPtrSet VisitedOVESourceExprs; + llvm::SmallPtrSet UnsafeBitCastsToSkip; + llvm::SmallPtrSet FBPtrCastsToSkip; + + void VisitChildren(const Stmt *S) { + for (const Stmt *ChildS : S->children()) + if (ChildS) + Visit(ChildS); + } + + // Individual checks performed on each unsafe operation. + void checkSingleEntityFlowingToIndexableLocalVariable(const Stmt *UnsafeOp, + const Expr *Operand, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleOp( + const Stmt *UnsafeOp, UnsafeOpKind Kind, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleDeref( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + bool + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, bool TestOnly); + + using ExtraDynCountLogicFn = std::function &)>; + + void checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, ExtraDynCountLogicFn Predicate); + + void + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + // Run all individual checks. + void handleUnsafeOperation(const Stmt *UnsafeOp, const Expr *Operand, + UnsafeOpKind Kind) { + checkSingleEntityFlowingToIndexableLocalVariable(UnsafeOp, Operand, Kind); + } + + bool ExprIsConstantZero(const Expr *E) { + auto Result = EvaluateAsInt(E); + if (!Result) + return false; + if (Result->isZero()) + return true; + return false; + } + + std::optional EvaluateAsInt(const Expr *E) { + Expr::EvalResult Result; + if (E->isValueDependent()) { + // Expr::EvaluateAsInt will assert if we try to call it in this + // case so just give up. + return std::optional(); + } + + bool success = E->EvaluateAsInt(Result, Ctx); + if (!success) + return std::optional(); + if (!Result.Val.isInt()) + return std::optional(); + return std::optional(Result.Val.getInt()); + } + + /// Determines if the provided expression is a __bidi_indexable pointer + /// that only allows access to it's 0th element. + /// + /// \param E - The expression to visit + /// + /// \return Tuple (X, Y). + /// X will be true iff `E` has type `T* __bidi_indexable` and has the bounds + /// of a `U* __single` where `sizeof(U) == sizeof(T)` and false otherwise. + /// + /// If X is true then Y will be the __single pointer type used for the bounds + /// of the __bidi_indexable pointer. + std::tuple + IsWidePointerWithBoundsOfSingle(const Expr *) const; + + bool IsReallySinglePtr(const QualType Ty) const { + if (!Ty->isSinglePointerType()) + return false; + + // Unfortunately __counted_by and friends, and __terminated_by use sugar + // types wrapping a __single so we need to check for those explicitly and + // bail in those cases. + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; + + return true; + }; + + bool FindSingleEntity( + const Expr *Def, const Expr *AssignmentExpr, + QualType SingleTyUsedForBidiBounds, + llvm::SmallVectorImpl + &Entities); + +public: + UnsafeOperationVisitor(BoundsSafetySuggestionHandler &Handler, + const DefinitionMap &DefMap, Sema &S) + : Handler(Handler), DefMap(DefMap), S(S), Ctx(S.getASTContext()) {} + + void VisitStmt(const Stmt *S) { + // Recurse normally. + VisitChildren(S); + } + + void reset() { + VisitedOVESourceExprs.clear(); + UnsafeBitCastsToSkip.clear(); + } + + // Unevaluated context visitors + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *) { + // Unevaluated context such as sizeof()/alignof()/__alignof + return; + } + + void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE) { + // Only the result expr is evaluated in `_Generic`. + // E.g. + // _Generic(0, // Not evaluated + // int: foo(), // Evaluated + // char: bar() // Not evaluated + // ); + Visit(GSE->getResultExpr()); + } + + // These are the individual unsafe operations we'll react upon. + void VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE); + void VisitUnaryOperator(const UnaryOperator *UO); + void VisitBinaryOperator(const BinaryOperator *BO); + void VisitMemberExpr(const MemberExpr *ME); + void VisitOpaqueValueExpr(const OpaqueValueExpr *OVE); + void VisitDeclStmt(const DeclStmt *DS); + void VisitReturnStmt(const ReturnStmt *RS); + void VisitCallExpr(const CallExpr *CE); + void VisitCastExpr(const CastExpr *CE); +}; + +} // namespace + +std::tuple +UnsafeOperationVisitor::IsWidePointerWithBoundsOfSingle(const Expr *E) const { + const auto Failure = std::make_tuple(false, QualType()); + if (!E->getType()->isPointerTypeWithBounds()) + return Failure; + + // Find the top-most BoundsSafetyPointerCast + const Expr *Curr = E; + const Expr *Prev = nullptr; + const CastExpr *FBPtrCast = nullptr; + while (Curr && Curr != Prev) { + Prev = Curr; + // Walk through ParenExprs + if (const auto *PE = dyn_cast(Curr)) { + Curr = PE->getSubExpr(); + continue; + } + // BoundsSafetyPointerCast can be on either ImplicitCastExpr or + // ExplicitCastExpr so use `CastExpr` which covers both. + if (const auto *CE = dyn_cast(Curr)) { + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) { + // Walk through all other casts. + Curr = CE->getSubExpr(); + continue; + } + FBPtrCast = CE; + break; + } + } + + if (!FBPtrCast) + return Failure; // Failed to find BoundsSafetyPointerCast + + // Check that the BoundsSafetyPointerCast acts on a __single pointer. + const auto SinglePtrQualTy = FBPtrCast->getSubExpr()->getType(); + if (!IsReallySinglePtr(SinglePtrQualTy)) + return Failure; + + // The resulting `T* __bidi_indexable` of E will get the bounds + // of a `U* __single`. + // + // Now check if `sizeof(T) >= sizeof(U)`. If that's the case then + // `E[]` will be out-of-bounds (where + // `E` is the expression `E`). + // + // When `sizeof(T) <= sizeof(U)` then `E[0]` will be in-bounds. + // + // When `sizeof(T) > sizeof(U)` then `E[0]` will also be out-of-bounds. + // However, due to rdar://119744147 generated code won't trap, so we currently + // don't try to emit a warning in that situation either (rdar://119775862). + + // Use getAs Walk through AttributedType sugar. E.g.: + // + // AttributedType 0x120024c90 'uint8_t *__bidi_indexable' sugar + // `-PointerType 0x120024c60 'uint8_t *__bidi_indexable' + const auto *IndexablePtrType = E->getType()->getAs(); + assert(IndexablePtrType); + + const auto TypeUsedForIndexing = IndexablePtrType->getPointeeType(); + const auto TypeUsedForIndexingSize = + Ctx.getTypeSizeOrNull(TypeUsedForIndexing); + if (TypeUsedForIndexingSize == 0) { + // This situation already raises `warn_bounds_safety_single_bitcast_lose_bounds` + // (if there's an explicit cast to a pointer with a sized type) or + // `err_bounds_safety_incomplete_single_to_indexable` elsewhere. + return Failure; + } + + // Walk through AttributedType sugar + const auto *SinglePointerTy = SinglePtrQualTy->getAs(); + assert(SinglePointerTy); + + const auto SinglePointeeType = SinglePointerTy->getPointeeType(); + const auto SinglePointeeTypeSize = Ctx.getTypeSizeOrNull(SinglePointeeType); + if (SinglePointeeTypeSize == 0) { + // This situation isn't legal and raises + // `err_bounds_safety_incomplete_single_to_indexable` + // elsewhere. + return Failure; + } + + if (TypeUsedForIndexingSize >= SinglePointeeTypeSize) + return std::make_tuple(true, SinglePointeeType); + + return Failure; +} + +bool UnsafeOperationVisitor::FindSingleEntity( + const Expr *Def, const Expr *AssignmentExpr, QualType SinglePointeeTy, + llvm::SmallVectorImpl + &SingleEntities) { + assert(IsReallySinglePtr(Def->getType())); + + // Consider the different ways a __single can end up in the variable + // definition + + // Definition comes directly from a variable + if (const auto *DRE = dyn_cast(Def)) { + const auto *SingleDecl = DRE->getDecl(); + if (!SingleDecl) + return false; + const auto *SingleVarDecl = dyn_cast(SingleDecl); + if (!SingleVarDecl) + return false; + + if (const auto *Param = dyn_cast(SingleVarDecl)) { + // Variable definition is from a __single parameter + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + Parameter, + /*Entity*/ Param, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + if (SingleVarDecl->hasGlobalStorage() && !SingleVarDecl->isStaticLocal()) { + // Variable definition is from a __single global + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + GlobalVar, + /*Entity*/ SingleVarDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + if (SingleVarDecl->isLocalVarDecl() || SingleVarDecl->isStaticLocal()) { + // Variable definition is from a local __single or a + // "locally scoped" static __single. + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + LocalVar, + /*Entity*/ SingleVarDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + return false; + } + + // __single comes from value returned by function call + if (const auto *CE = dyn_cast(Def)) { + const auto *DirectCallDecl = CE->getDirectCallee(); + if (!DirectCallDecl) { + // Don't support indirect calls for now. + return false; + } + if (DirectCallDecl->hasAttr() && + IsReallySinglePtr(DirectCallDecl->getReturnType())) { + // Functions declared like + // void * custom_malloc(size_t s) __attribute__((alloc_size(1))) + // + // are currently are annotated as returning `void *__single` rather + // than `void *__sized_by(s)`. To make the right thing happen at call + // sites `BoundsSafetyPointerPromotionExpr` is used to generate a pointer + // with the appropriate bounds from the `void *__single`. For functions + // like this the warning needs to be suppressed because from the user's + // perspective the returned value is not actually __single. + // + // This code path can be deleted once allocating functions are properly + // annotated with __sized_by_or_null. rdar://117114186 + return false; + } + + assert(IsReallySinglePtr(DirectCallDecl->getReturnType())); + + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + FunctionCallReturnValue, + /*Entity*/ DirectCallDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + auto HasSingleFromStructOrUnion = [&](const Expr *E) -> bool { + if (const auto *ME = dyn_cast(E)) { + if (const auto *FD = dyn_cast(ME->getMemberDecl())) { + BoundsSafetySuggestionHandler::AssignmentSourceKind AssignmentKind; + if (FD->getParent()->isUnion()) { + AssignmentKind = + BoundsSafetySuggestionHandler::AssignmentSourceKind::UnionMember; + } else { + assert(FD->getParent()->isStruct()); + AssignmentKind = + BoundsSafetySuggestionHandler::AssignmentSourceKind::StructMember; + } + + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ AssignmentKind, + /*Entity*/ FD, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + } + return false; + }; + + // __single comes from array access + if (const auto *ASE = dyn_cast(Def)) { + // The `IgnoreImpCasts()` on `ASE->getBase()` is necessary here to walk + // through MaterializeSequenceExpr, BoundsSafetyPointerPromotionExpr, + // OpaqueValueExpr, and ImplicitCastExpr. + const Expr *BaseExpr = ASE->getBase()->IgnoreImpCasts(); + + // Array access is on a variable. + if (const auto *DRE = dyn_cast(BaseExpr)) { + if (const auto *ArrayDecl = dyn_cast(DRE->getDecl())) { + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + ArrayElement, + /*Entity*/ ArrayDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + } + + // Array access is on struct/union member + if (HasSingleFromStructOrUnion(BaseExpr)) + return true; + } + + // __single comes from direct access to a struct/union member + if (HasSingleFromStructOrUnion(Def)) + return true; + + return false; +} + +void UnsafeOperationVisitor::checkSingleEntityFlowingToIndexableLocalVariable( + const Stmt *UnsafeOp, const Expr *Operand, UnsafeOpKind Kind) { + + const Expr *PreviousOperand = nullptr; + const Expr *CurrentOperand = Operand; + + while (CurrentOperand != PreviousOperand) { + PreviousOperand = CurrentOperand; + if (const auto *OVE = dyn_cast(CurrentOperand)) { + CurrentOperand = OVE->getSourceExpr(); + continue; + } + // This is quite broad, but such transformation typically results in + // an expression with roughly the same value, i.e. it refers to the same + // object. The only exception from this rule is, implicit casts of + // CK_LValueToRValue kind, which constitute memory load operations. + // But we don't expect any of that coming in. + CurrentOperand = CurrentOperand->IgnoreParenCasts(); + } + + const auto *VarDRE = dyn_cast(CurrentOperand); + if (!VarDRE) + return; + + const auto *Var = dyn_cast(VarDRE->getDecl()); + if (!Var || !Var->isLocalVarDecl()) + return; + + QualType VarTy = Var->getType(); + + // It might be that we've discarded an explicit cast from an integer + // to a pointer, so let's double-check. + if (!VarTy->isPointerTypeWithBounds()) + return; + + // Check that the variable is *implicitly* __bidi_indexable. + // Explicitly __bidi_indexable variables are covered by + // -Wbounds-attributes-implicit-conversion-single-to-explicit-indexable. + // + // We use `hasAttr` to walk through additional AttributedTypes that may be + // present. + if (!VarTy->hasAttr(attr::PtrAutoAttr)) + return; + + auto DefI = DefMap.find(Var); // The map is const, can't use []. + if (DefI == DefMap.end()) + return; + + const DefinitionList &Defs = DefI->second; + + llvm::SmallVector + SingleEntities; + + if (Defs.size() == 0) + return; + + // Walk over all the potential definitions that the __bidi_indexable variable + // might take. If they are all __single and can all be identified as + // an "entity" then a warning is emitted. + for (const auto *Def : Defs) { + // Skip emitting warnings if any of the definitions aren't T* + // __bidi_indexable with the bounds of a T* __single. + bool IsWPWBOS = false; + QualType SingleTyUsedForBidiBounds; + std::tie(IsWPWBOS, SingleTyUsedForBidiBounds) = + IsWidePointerWithBoundsOfSingle(Def); + if (!IsWPWBOS) + return; + + // It's ok to walk through all casts (including explicit) at this point + // because `IsWidePointerWithBoundsOfSingle` has already checked that this + // is a scenario where we should warn. + const Expr *DefWithoutParensAndCasts = Def->IgnoreParenCasts(); + + // Skip emitting warnings if any of the definitions are not "effectively" + // __single. We can only be sure a `__single` flowed into an indexable + // variable in this case. + if (!IsReallySinglePtr(DefWithoutParensAndCasts->getType())) + return; + + const Expr *AssignmentExpr = nullptr; + if (Def != Var->getInit()) { + // The definition comes from assignment (i.e. not an initializer). + AssignmentExpr = DefWithoutParensAndCasts; + } + // Skip emitting the warning if the entity for any of the definitions can't + // be found. + if (!FindSingleEntity(DefWithoutParensAndCasts, AssignmentExpr, + SingleTyUsedForBidiBounds, SingleEntities)) + return; + } + + // Dispatch based on the unsafe operation. + switch (Kind) { + case BoundsSafetySuggestionHandler::UnsafeOpKind::Arithmetic: + checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + UnsafeOp, Var, SingleEntities); + break; + case BoundsSafetySuggestionHandler::UnsafeOpKind::Index: + checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + UnsafeOp, Var, SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Deref: + checkSingleEntityFlowingToIndexableLocalVariableHandleDeref(UnsafeOp, Var, + SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::MemberAccess: + checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + UnsafeOp, Var, SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Assignment: + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Return: + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::CallArg: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + UnsafeOp, Operand, Var, SingleEntities, Kind); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Cast: + checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + UnsafeOp, Operand, Var, SingleEntities, Kind); + break; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + default: + llvm_unreachable("Unhandled UnsafeOpKind"); +#pragma clang diagnostic pop + } +} + +static bool AccessingElementZeroIsOOB( + ASTContext &Ctx, const QualType EltTy, + llvm::SmallVectorImpl + &SingleEntities) { + auto EltSize = Ctx.getTypeSizeOrNull(EltTy); + if (EltSize == 0) + return false; + // If the EltTy used for dereferencing is larger than all the potential + // __single bounds i then this out-of-bounds. This currently won't trap in + // some cases due to rdar://104845295 but it will be in the future so we + // should still warn. + bool DefinitelyOOB = std::all_of( + SingleEntities.begin(), SingleEntities.end(), + [&](const BoundsSafetySuggestionHandler::SingleEntity &S) -> bool { + auto SingleSize = Ctx.getTypeSizeOrNull(S.SinglePointeeTy); + if (SingleSize == 0) + return false; + return EltSize > SingleSize; + }); + return DefinitelyOOB; +} + +static QualType GetLargestSinglePointee( + llvm::SmallVectorImpl + &SingleEntities, + ASTContext &Ctx) { + QualType LargestSinglePointee = SingleEntities.front().SinglePointeeTy; + auto LargestPointeeSizeInBits = Ctx.getTypeSizeOrNull(LargestSinglePointee); + for (const auto &SingleEntity : SingleEntities) { + const auto CurrentPointeeTy = SingleEntity.SinglePointeeTy; + auto CurrentPointeeSizeInBits = Ctx.getTypeSizeOrNull(CurrentPointeeTy); + if (CurrentPointeeSizeInBits > LargestPointeeSizeInBits) { + LargestSinglePointee = CurrentPointeeTy; + LargestPointeeSizeInBits = CurrentPointeeSizeInBits; + } + } + return LargestSinglePointee; +} + +struct PointerArithInBoundsInfo { + // Any effective offset >= to this is out-of-bounds. + // Note the offset is assumed to be in units of the pointee + // type used for the pointer arithmetic. + // + // The "effective offset" is the offset assuming the operation + // was an addition operation. + size_t MinimumOOBPositiveOffset; + + BoundsSafetySuggestionHandler::PtrArithOOBKind IsOOB; + + PointerArithInBoundsInfo() + : MinimumOOBPositiveOffset(0), + IsOOB(BoundsSafetySuggestionHandler::PtrArithOOBKind::UNKNOWN) {} +}; + +/// Compute if pointer arithmetic would be in bounds and other related info. +/// +/// \param PointeeTySizeInBits The pointee size used for pointer arithmetic. +/// \param AvailableBits The number bits available in the memory pointed to by +/// the pointer. \param Offset If set it is the offset that would be used. +/// +/// Example: +/// +/// PointeeTySizeInBits = 1*8 = 8 +/// AvailableBits = 4*8 = 32 +/// Offset = Unset +/// OpIsIncrement = true +/// OffsetIsSignedTy = false (size_t is unsigned) +/// +/// Assume sizeof(int) = 4 and sizeof(char) = 1. This corresponds to +/// +/// ``` +/// void foo(int* p, size_t offset) { +/// int* local = p; +/// ((char*)local + offset); +/// } +PointerArithInBoundsInfo ComputeIfPointerArithIsInBounds( + size_t PointeeTySizeInBits, size_t AvailableBits, + std::optional Offset, bool OpIsIncrement, bool OffsetIsSignedTy) { + PointerArithInBoundsInfo Result; + using PtrArithOOBKind = BoundsSafetySuggestionHandler::PtrArithOOBKind; + + // Compute the smallest offset that would be out-of-bounds. + Result.MinimumOOBPositiveOffset = AvailableBits / PointeeTySizeInBits; + + if (Result.MinimumOOBPositiveOffset == 0) { + // Special case: This means any offset >= 0 would cause an out-of-bounds + // access. This means **any** offset would be out-of-bounds. + // + // This will happen if PointeeTySizeInBits > Availablebits. + // + // E.g. + // ``` + // uint32_t* local; + // *((uint64_t*)local + offset) = 0; + // ``` + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_BASE_OOB; + return Result; + } + + if (!Offset.has_value()) { + // The offset isn't known + + if (OffsetIsSignedTy) { + // Effective offset can be negative or positive or zero + Result.IsOOB = PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO; + } else { + // Effective offset can be positive or zero + if (OpIsIncrement) { + // Addition operation + // Effective offset can be zero or positive (cannot be negative) + Result.IsOOB = PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET; + } else { + // Subtract operation + // Effective offset can be negative or zero (cannot be positive) + Result.IsOOB = PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO; + } + } + return Result; + } + + // Offset is a constant + + // Compute the "EffectiveOffset" which is the offset that would be used + // assuming the operation was always a pointer increment operation. I.e.: + // + // ptr + EffectiveOffset + // + APInt EffectiveOffset = Offset.value(); + if (!OpIsIncrement) { + // The operation does pointer subtraction. + // Note if `EffectiveOffset.isMinSignedValue()` + // then the negation will get the same value back. + + EffectiveOffset.negate(); + } + + if (EffectiveOffset.isNegative()) { + // Any negative offset is out-of-bounds + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET; + return Result; + } + + // If this condition is true + // + // EffectiveOffset >= (AvailableBits / PointeeTySizeInBits) + // + // then then a memory access at the offset would access out-of-bounds + // memory. + assert(!EffectiveOffset.isNegative()); + auto MinimumOOBOffset = + APInt(EffectiveOffset.getBitWidth(), Result.MinimumOOBPositiveOffset); + + if (EffectiveOffset.uge(MinimumOOBOffset)) + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET; + else + Result.IsOOB = PtrArithOOBKind::NEVER_OOB; + + return Result; +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + auto *ASE = cast(UnsafeOp); + // Determine the pointee sized used for indexing + const auto BasePtrTy = ASE->getBase()->getType(); + const auto IndexingPointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // This path can be hit if the __bidi_indexable is casted to a different + // attribute type before being indexed. E.g.: + // + // ``` + // int* __single p; + // int* local = p; + // ((int* __single) local)[0] = 0; + // ``` + // + // The analysis here currently assumes the base pointer is __bidi_indexable + // so don't try to proceed further. + return; + } + + const auto IndexingPointeeTyInBits = Ctx.getTypeSizeOrNull(IndexingPointeeTy); + assert(IndexingPointeeTyInBits > 0); + + // Determine the max possible bounds that the __bidi_indexable could + // be storing. We use the maximum to reduce chances of false positives. + const auto LargestSinglePointeeTy = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeTyInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointeeTy); + + // Try to evaluate the index as a constant + const auto KnownOffset = EvaluateAsInt(ASE->getIdx()); + bool IndexTyIsSigned = ASE->getIdx()->getType()->isSignedIntegerType(); + const auto PABInfo = ComputeIfPointerArithIsInBounds( + IndexingPointeeTyInBits, LargestSinglePointeeTyInBits, KnownOffset, + /*OpIsIncrement=*/true, /*OffsetIsSignedTy=*/IndexTyIsSigned); + + if (PABInfo.IsOOB == PtrArithOOBKind::NEVER_OOB) + return; + + if (PABInfo.IsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // Accessing any index is out-of-bounds + const auto *IndexableVarExpr = ASE->getBase(); + // Accessing element is guaranteed to be out-of-bounds. This currently + // won't trap due to rdar://104845295 but it will be in the future so we + // should still warn. + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::Index); + return; + } + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Index, + PABInfo.IsOOB, PABInfo.MinimumOOBPositiveOffset); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + const Expr *E = cast(UnsafeOp); + const auto PointeeTy = E->getType()->getAs()->getPointeeType(); + if (PointeeTy->isIncompleteType()) + return; + const auto PointeeTySizeInBits = Ctx.getTypeSizeOrNull(PointeeTy); + + const auto LargestSinglePointee = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointee); + + size_t MinimumOOBOffset = 0; + PtrArithOOBKind IsOOB = PtrArithOOBKind::UNKNOWN; + + auto ShouldWarn = [&](std::optional KnownOffset, bool OpIsIncrement, + bool OffsetIsSignedTy) -> bool { + auto Info = ComputeIfPointerArithIsInBounds( + PointeeTySizeInBits, LargestSinglePointeeInBits, KnownOffset, + OpIsIncrement, OffsetIsSignedTy); + + MinimumOOBOffset = Info.MinimumOOBPositiveOffset; + IsOOB = Info.IsOOB; + assert(IsOOB != PtrArithOOBKind::UNKNOWN); + + if (IsOOB == PtrArithOOBKind::NEVER_OOB) + return false; + + return true; + }; + + if (auto *UO = dyn_cast(UnsafeOp)) { + std::optional IsIncrement; + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PreInc: + IsIncrement = true; + break; + case UO_PreDec: + case UO_PostDec: + IsIncrement = false; + break; + default: + llvm_unreachable("Unhandled UnaryOperator"); + } + assert(IsIncrement.has_value()); + if (!ShouldWarn(/*KnownOffset=*/APInt(/*numBits=*/64, 1), + /*OpIsIncrement=*/IsIncrement.value(), + /*OffsetIsSignedTy=*/false)) + return; + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Arithmetic, IsOOB, + MinimumOOBOffset); + + return; + } + if (auto *BO = dyn_cast(UnsafeOp)) { + const Expr *Offset; + + bool IsIncrement = false; + switch (BO->getOpcode()) { + case BO_Add: + // n + ptr or ptr + n + case BO_AddAssign: + // ptr += n (i.e. ptr = ptr + n) + IsIncrement = true; + break; + case BO_Sub: + // ptr - n + case BO_SubAssign: + // ptr -= n (i.e. ptr = ptr - n) + IsIncrement = false; + break; + default: + llvm_unreachable("Unhandled BinaryOperator"); + } + + if (BO->getLHS()->getType()->isPointerType()) { + Offset = BO->getRHS(); + } else { + Offset = BO->getLHS(); + } + assert(Offset->getType()->isIntegralOrEnumerationType()); + bool OffsetIsSignedTy = + Offset->getType()->isSignedIntegerOrEnumerationType(); + + auto EvaluatedOffset = EvaluateAsInt(Offset); + if (!ShouldWarn(/*KnownOffset=*/EvaluatedOffset, + /*OpIsIncrement=*/IsIncrement, + /*OffsetIsSignedTy=*/OffsetIsSignedTy)) + return; + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Arithmetic, IsOOB, + MinimumOOBOffset); + return; + } + llvm_unreachable("Unhandled UnsafeOp"); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleDeref( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + + if (auto *UO = dyn_cast(UnsafeOp)) { + assert(UO->getOpcode() == UO_Deref); + const auto *BasePtrTy = UO->getSubExpr()->getType()->getAs(); + assert(BasePtrTy); + const auto BasePointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // It's possible that the there are casts that change the attribute type. + // E.g. + // + // void consume(int* p) { + // int* ptr = p; + // *((int* __single) ptr) = 0; + // } + return; + } + + if (!AccessingElementZeroIsOOB(Ctx, BasePointeeTy, SingleEntities)) + return; + + const auto *IndexableVarExpr = UO->getSubExpr(); + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::Deref); + return; + } + + llvm_unreachable("Unhandled UnsafeOp"); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + const auto *ME = cast(UnsafeOp); + const auto *BasePtrTy = ME->getBase()->getType()->getAs(); + assert(BasePtrTy); + const auto BasePointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // It's possible that the there are casts that change the attribute type. + // E.g. + // + // void consume(struct Foo* p) { + // struct Foo* ptr = p; + // *((struct Bar* __single) ptr) = 0; + // } + return; + } + + // This is rather subtle but access to **any** field in the struct (not just + // the fields above the upper bound of the wide pointer) will trap because + // `CodeGenFunction::EmitMemberExpr` checks that "one-past-the-end" (assuming + // the pointee type of the wide pointer) is <= the upper bound. This check + // will always fail if the bounds stored in the wide pointer are for a smaller + // type. + // + // So it is not sufficient to check if the field being accessed is above the + // upper bound. Instead check if **any** part of the struct will be + // out-of-bounds which corresponds directly to the check that + // `CodeGenFunction::EmitMemberExpr` does. + if (!AccessingElementZeroIsOOB(Ctx, BasePointeeTy, SingleEntities)) + return; + + const auto *IndexableVarExpr = ME->getBase(); + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::MemberAccess); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + // Note. This is "escape" in the sense that the indexable variable is being + // assigned to something else which means it "escapes" the analysis (the + // bounds are no longer tracked due to the fact transitive assignments are not + // tracked) and not necessarily "escapes" in the sense that the pointer + // escapes the current function (although that is what happens in some cases). + + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); + + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + const auto IndexableEltTy = + IndexableVar->getType()->getAs()->getPointeeType(); + + // Note: We deliberately don't check the pointer attributes on `Operand` + // as `IndexableVar` could have been casted to have a different pointer + // attribute and we still want to warn about it escaping the analysis. + + // Using `IndexableEltTy` as the element type is "technically" wrong. + // The correct element type is actually: + // + // Operand->getType()->getPointeeType() + // + // However, doing this causes many more warnings to be emitted because + // we will warn about unsafe casts at the use site that we already warned + // about. E.g.: + // + // ``` + // int* consume(char*p) { + // char* local = p; + // // The cast already causes a warning to be emitted. + // return (int*) local; + // } + // ``` + // + // To avoid emitting these extra warnings we use `IndexableEltTy` instead + // which indirectly avoids emitting warnings for casts we already warned about + // but still allows us to warn about when the unsafe cast at assignment to + // the local `__bidi_indexable.` E.g.: + // + // ``` + // int* consume(char* p) { + // int* local = (int* __bidi_indexable)(char* __bidi_indexable) p; + // return local; // Warn here + // } + // ``` + // + // Revisiting this design decision is tracked by rdar://123654605. + // + if (!AccessingElementZeroIsOOB(Ctx, IndexableEltTy, SingleEntities)) + return; + // The local __bidi_indexable does not have bounds sufficient to access a + // single element so warn when this pointer escapes the analysis. + + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, Operand, Kind); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + // Look for unsafe `__bidi_indexable -> __single conversion (causes a bounds + // check). + auto *CE = cast(UnsafeOp); + + if (!IsReallySinglePtr(CE->getType())) { + return; + } + + assert(CE->getSubExpr() == Operand); + + // Make sure this is a `__bidi_indexable/__indexable -> __single` conversion + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) + return; + const auto OperandTy = Operand->getType(); + if (!OperandTy->isPointerTypeWithBounds()) + return; + const auto EltTy = OperandTy->getAs()->getPointeeType(); + + if (!AccessingElementZeroIsOOB(Ctx, EltTy, SingleEntities)) + return; + + // At this cast a bounds check will be emitted. The pointee type on the + // cast is larger than the bounds that will be stored in `IndexableVar` at + // runtime. This should be a trap but currently isn't due to rdar://104845295 + // but will be in the future so warn about this. + Handler.handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand); + + // Try to suppress warnings about unsafe bit casts in the expr tree of the + // operand given that we've already warned about this trapping cast. + const auto IndexableVarPointeeTy = + IndexableVar->getType()->getAs()->getPointeeType(); + if (EltTy != IndexableVarPointeeTy) { + const Expr *Current = Operand; + const Expr *Previous = nullptr; + while (Current != Previous) { + Previous = Current; + if (const auto *PE = dyn_cast(Current)) { + Current = PE->getSubExpr(); + continue; + } + if (const auto *CE = dyn_cast(Current)) { + if (CE->getCastKind() == clang::CK_BitCast) { + if (checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + CE, CE->getSubExpr(), IndexableVar, SingleEntities, Kind, + /*TestOnly=*/true)) { + // If this bit cast was visited it would be warned about so add it + // to the set of bitcasts to skip. + UnsafeBitCastsToSkip.insert(CE); + } + } + Current = CE->getSubExpr(); + } + } + } +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind, + /*TestOnly=*/false); + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind, + /*Predicate=*/nullptr); +} + +bool UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, bool TestOnly) { + + // Report casts that bitcast __bidi_indexable/__indexable pointer + // to a larger pointee type where we know the bounds at runtime will + // be too small. + auto *CE = cast(UnsafeOp); + + if (UnsafeBitCastsToSkip.contains(CE)) { + // This cast was already reported elsewhere so skip it. + return false; + } + + if (CE->getCastKind() != clang::CK_BitCast) + return false; + + // The result needs to be a __bidi_indexable/__indexable pointer + if (!CE->getType()->isPointerTypeWithBounds()) + return false; + + // This is a BitCast so the pointer attribute should not have changed. + assert(CE->getSubExpr()->getType()->isPointerTypeWithBounds()); + + // Go through all __single entities and find the largest pointee type. + QualType LargestSinglePointee = GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeSizeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointee); + if (LargestSinglePointeeSizeInBits == 0) + return false; + + // Get size of pointee of CastExpr + auto CEPointeeTy = CE->getType()->getAs()->getPointeeType(); + const auto CEPointeeTySizeInBits = Ctx.getTypeSize(CEPointeeTy); + if (CEPointeeTySizeInBits == 0) + return false; + + if (CEPointeeTySizeInBits <= LargestSinglePointeeSizeInBits) + return false; + + if (TestOnly) + return true; + + // Found unsafe bitcast + UnsafeBitCastsToSkip.insert(CE); + Handler.handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand); + return true; +} + +using WillTrapKind = clang::BoundsSafetySuggestionHandler::WillTrapKind; +static WillTrapKind +TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + const CountAttributedType *DCPT, const APInt &CountExprAsConstant, + size_t LargestSinglePointeeTySizeInBits, size_t CastPointeeTySizeInBits) { + const auto CountExprBitWidth = CountExprAsConstant.getBitWidth(); + if (CountExprAsConstant.isZero()) { + // The bounds in the `__bidi_indexable` are guaranteed to be >= 0 + // bytes/objects so this can't trap. + return WillTrapKind::NoTrap; + } + if (DCPT->isCountInBytes()) { + // __sized_by(_or_null) + auto LargestSinglePointeeTySizeInBytes = + llvm::APInt(CountExprBitWidth, LargestSinglePointeeTySizeInBits / 8); + if (CountExprAsConstant.ule(LargestSinglePointeeTySizeInBytes)) { + // The bounds of the __bidi_indexable are larger or equal to the + // constant byte count. In this case the only possible trap is caused + // by the pointer being null. + if (DCPT->isOrNull()) { + // Won't trap even if nullptr passed + return WillTrapKind::NoTrap; + } + // Won't trap unless ptr is null + return WillTrapKind::TrapIffPtrNull; + } + + // The bounds of the __bidi_indexable are smaller than the constant + // byte count expected. So the bounds are insufficient. Whether or not + // this traps depends on the attribute type and the pointer value. + if (DCPT->isOrNull()) { + // If the pointer is not null then the bounds are insufficient, so + // this traps. + return WillTrapKind::TrapIffPtrNotNull; + } + // Always traps + return WillTrapKind::Trap; + } + + // __counted_by(_or_null) + const auto CastPointeeTySizeInBitsAP = + APInt(CountExprBitWidth, CastPointeeTySizeInBits); + const auto MinimumBufferSizeInBitsAP = + CastPointeeTySizeInBitsAP * CountExprAsConstant; + const auto LargestSinglePointeeTySizeInBitsAP = + APInt(CountExprBitWidth, LargestSinglePointeeTySizeInBits); + + if (LargestSinglePointeeTySizeInBitsAP.uge(MinimumBufferSizeInBitsAP)) { + // The number of bits the __counted_by_(or_null) expects is <= to + // the number of bits of the `__single` stored in the `__bidi_indexable` + // pointer. So at the point of conversion the bounds in the + // `__bidi_indexable` are big enough. + // + // If the pointee sizes are the same then this is just __counted_by(1) or + // __counted_by_or_null(1) +#ifndef NDEBUG + if (LargestSinglePointeeTySizeInBitsAP == CastPointeeTySizeInBitsAP) + assert(CountExprAsConstant.isOne()); +#endif + + if (DCPT->isOrNull()) { + // __counted_by_or_null: Won't trap. Even if nullptr passed + return WillTrapKind::NoTrap; + } + // __counted_by: Traps if and only if ptr is null + return WillTrapKind::TrapIffPtrNull; + } + + // LargestSinglePointeeTySizeInBitsAP < MinimumBufferSizeInBitsAP + // The number of bits the __counted_by_(or_null) expects is > + // the number of bits of the `__single` stored in the `__bidi_indexable`. + // So at the point of conversion the bounds in the `__bidi_indexable` + // are **not** big enough. + // + // If the pointee sizes are the same then this is + // `__counted_by()` or `__counted_by_or_null()` + // where is > 1 +#ifndef NDEBUG + if (LargestSinglePointeeTySizeInBitsAP == CastPointeeTySizeInBitsAP) + assert(CountExprAsConstant.ugt(APInt(CountExprBitWidth, 1))); +#endif + + if (DCPT->isOrNull()) { + // Traps if and only if the ptr is non-null + return WillTrapKind::TrapIffPtrNotNull; + } + + // Always traps + return WillTrapKind::Trap; +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, ExtraDynCountLogicFn Predicate) { + + // Report casts that __bidi_indexable/__indexable pointers + // to __counted_by/__sized_by + auto *CE = cast(UnsafeOp); + + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) + return; + + if (FBPtrCastsToSkip.contains(CE)) + return; + + // Check for __bidi_indexable/__indexable being converted + assert(Operand == CE->getSubExpr()); + if (!Operand->getType()->isPointerTypeWithBounds()) + return; + + if (!CE->getType()->isBoundsAttributedType()) + return; + + const auto *DCPT = CE->getType()->getAs(); + if (!DCPT) + return; + const auto DCPTQualTy = CE->getType(); + + // Use the largest pointee type out of all the potential __single to avoid + // potential false positives. This choice can cause false negatives. + const auto LargestSinglePointeeTy = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeTySizeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointeeTy); + const auto CastPointeeTy = DCPT->desugar()->getPointeeType(); + const auto CastPointeeTySizeInBits = Ctx.getTypeSizeOrNull(CastPointeeTy); + + // Try to see if the count expr is a constant + WillTrapKind WillTrap = WillTrapKind::Unknown; + auto CountExprAsConstant = + EvaluateAsInt(DCPT->getCountExpr()->IgnoreParens()); + if (CountExprAsConstant) { + WillTrap = TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + DCPT, *CountExprAsConstant, LargestSinglePointeeTySizeInBits, + CastPointeeTySizeInBits); + } + + // Compute the maximum count/size expression that can be used without leading + // to a trap. Any count/size > will trap. + // E.g. + // int foo(int* __counted_by(size) b, size_t size); + // + // void use_foo(int* b, size_t s) { + // // The maximum value `s` can be is 1. + // foo(b, s); + // } + size_t MaxSafeSizeOrCount = 0; + if (DCPT->isCountInBytes()) { + // __sized_by(or_null) + MaxSafeSizeOrCount = LargestSinglePointeeTySizeInBits / 8; + } else { + // __counted_by(_or_null) + // + // If the pointee types are the same size this is 1. + // If the __single pointee size is < DCPT pointee size then this is 0 + // If the __single pointee size is > DCPT pointee size then this >= 1. + MaxSafeSizeOrCount = + LargestSinglePointeeTySizeInBits / CastPointeeTySizeInBits; + } + + // Run custom logic to suppress this warning or determine that the count + // expression is a constant. + if (Predicate) { + WillTrap = Predicate(WillTrap, LargestSinglePointeeTySizeInBits, + CastPointeeTySizeInBits, CountExprAsConstant); + } + + // Make sure we don't visit this cast again. + FBPtrCastsToSkip.insert(CE); + + // Technically if `Kind == UnsafeOpKind::Return` then we will never trap due + // a bug where the bounds checks are missing (rdar://83900556). We ignore that + // problem here and let the handler decide what to do. + + switch (WillTrap) { + case WillTrapKind::NoTrap: + // Nothing to warn about. + return; + case WillTrapKind::TrapIffPtrNull: + // TODO: We should probably warn about this but we probably want to do this + // elsewhere (i.e. not in the analysis). + // For now just drop this. + return; + case WillTrapKind::Unknown: // Might trap + case WillTrapKind::Trap: // Definitely traps + case WillTrapKind::TrapIffPtrNotNull: // Traps in almost all cases + Handler.handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand, DCPTQualTy, + WillTrap, CountExprAsConstant, MaxSafeSizeOrCount); + break; + } +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + assert(Kind == UnsafeOpKind::CallArg || Kind == UnsafeOpKind::Return || + Kind == UnsafeOpKind::Assignment); + + const auto *DCPT = Operand->getType()->getAs(); + if (!DCPT) + return; + + // Set up code to lazily compute the count expression with call parameters + // replaced with arguments used at call site. + const Expr *CountExprUsingParams = nullptr; + const Expr *CountExprUsingArguments = nullptr; + const auto *Call = dyn_cast(UnsafeOp); + auto LazilyInitCountExprUsingArguments = [&]() { + if (CountExprUsingArguments) + return; + ExprResult Replaced = + ReplaceCountExprParamsWithArgsFromCall(CountExprUsingParams, Call, S); + if (auto *NewCountExpr = Replaced.get()) { + CountExprUsingArguments = NewCountExpr; + } + }; + + auto CallExprPredicate = + [&](WillTrapKind WillTrap, size_t LargestSinglePointeeTySizeInBits, + size_t CastPointeeTySizeInBits, + std::optional &CountExprAsConstant) -> WillTrapKind { + // This predicate tries to see if it can statically determine + // if the cast will fail. It does this by trying to see if the + // __counted_by/__sized_by size expression is a constant if the + // call arguments are substituted into the expression. + if (WillTrap != WillTrapKind::Unknown) { + // Trust the existing analysis + return WillTrap; + } + + LazilyInitCountExprUsingArguments(); + const auto CountExprUsingArgumentsAsConstant = + EvaluateAsInt(CountExprUsingArguments); + + if (CountExprUsingArgumentsAsConstant) { + CountExprAsConstant = + *CountExprUsingArgumentsAsConstant; // Pass back to caller + WillTrap = + TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + DCPT, *CountExprUsingArgumentsAsConstant, + LargestSinglePointeeTySizeInBits, CastPointeeTySizeInBits); + } + + return WillTrap; + }; + + ExtraDynCountLogicFn Predicate = nullptr; + // Currently only `CallArg` needs a special predicate. + if (Kind == UnsafeOpKind::CallArg) { + assert(Call); + CountExprUsingParams = DCPT->getCountExpr(); + Predicate = CallExprPredicate; + } + + // FIXME: Ideally we'd support constant evaluation for returns and + // variable assignment too. Unfortunately we don't anyway right now + // of knowing what values the variables referred to in the count expression + // could take. We'd need a reaching-definition analysis to do this. + + // Walk through the Operand to find potential CK_BoundsSafetyPointerCast's to + // warn about. Although the visitor will normally find this cast later on its + // done this way here so that when the cast is found the extra context (e.g. + // the cast happens at a call site) needed to emit the diagnostic. + const Expr *Current = Operand; + const Expr *Previous = nullptr; + while (Current != Previous) { + Previous = Current; + if (const auto *PE = dyn_cast(Current)) { + Current = PE->getSubExpr(); + continue; + } + if (const auto *CE = dyn_cast(Current)) { + if (CE->getCastKind() == clang::CK_BoundsSafetyPointerCast) { + // Found a cast we might want to warn about. + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + CE, CE->getSubExpr(), IndexableVar, SingleEntities, Kind, + Predicate); + } + Current = CE->getSubExpr(); + } + } +} + +void UnsafeOperationVisitor::VisitArraySubscriptExpr( + const ArraySubscriptExpr *ASE) { + handleUnsafeOperation(ASE, ASE->getBase(), UnsafeOpKind::Index); + + // Continue visitation normally. + VisitChildren(ASE); +} + +void UnsafeOperationVisitor::VisitUnaryOperator(const UnaryOperator *UO) { + + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PostDec: + case UO_PreInc: + case UO_PreDec: + handleUnsafeOperation(UO, UO->getSubExpr(), UnsafeOpKind::Arithmetic); + break; + case UO_Deref: + handleUnsafeOperation(UO, UO->getSubExpr(), UnsafeOpKind::Deref); + break; + default: + break; + } + + // Continue visitation normally. + VisitChildren(UO); +} + +void UnsafeOperationVisitor::VisitBinaryOperator(const BinaryOperator *BO) { + const Expr *Lhs = BO->getLHS(), *Rhs = BO->getRHS(); + QualType LhsTy = Lhs->getType(), RhsTy = Rhs->getType(); + + switch (BO->getOpcode()) { + case BO_Add: + // n + ptr, a special case that only works with BO_Add. + if (RhsTy->isAnyPointerType() && LhsTy->isIntegralOrEnumerationType()) { + handleUnsafeOperation(BO, Rhs, UnsafeOpKind::Arithmetic); + } + LLVM_FALLTHROUGH; // Fall through to handle ptr + n + case BO_Sub: + case BO_AddAssign: + case BO_SubAssign: + // ptr + n, ptr - n, ptr += n, ptr -= n, i.e. the typical case. + if (LhsTy->isAnyPointerType() && RhsTy->isIntegralOrEnumerationType()) { + handleUnsafeOperation(BO, Lhs, UnsafeOpKind::Arithmetic); + } + break; + case BO_Assign: { + if (RhsTy->isPointerType()) { + handleUnsafeOperation(BO, Rhs, UnsafeOpKind::Assignment); + } + break; + } + default: + break; + } + + // Continue visitation normally. + VisitChildren(BO); +} + +void UnsafeOperationVisitor::VisitMemberExpr(const MemberExpr *ME) { + if (ME->isArrow()) { + assert(ME->getBase()->getType()->isPointerType()); + handleUnsafeOperation(ME, ME->getBase(), UnsafeOpKind::MemberAccess); + } + + // Continue visitation normally. + VisitChildren(ME); +} + +void UnsafeOperationVisitor::VisitOpaqueValueExpr(const OpaqueValueExpr *OVE) { + // OpaqueValueExpr doesn't have any children so Visit() won't traverse the + // SourceExpr so we need to handle it manually here. + const auto *SrcExpr = OVE->getSourceExpr(); + if (!SrcExpr) + return; + + // OVEs wrap an expression that should only be evaluated once. Thus this + // visitor needs to make sure it only visits the SourceExpr once, otherwise it + // will warn about the same unsafe operation multiple times. + if (VisitedOVESourceExprs.contains(SrcExpr)) + return; // Don't visit again + + VisitedOVESourceExprs.insert(SrcExpr); + Visit(SrcExpr); +} + +void UnsafeOperationVisitor::VisitDeclStmt(const DeclStmt *DS) { + for (const Decl *D : DS->decls()) { + if (const auto *VD = dyn_cast(D)) { + if (const Expr *E = VD->getInit()) { + if (E->getType()->isPointerType()) { + handleUnsafeOperation(DS, E, UnsafeOpKind::Assignment); + } else if (const auto *ILE = dyn_cast(E)) { + // Struct/Union initializer. + // Note `InitListExpr` may be nested so iterative DFS + // is used to walk through nested `InitListExpr` and visit + // all the initializers. + llvm::SmallVector> WorkList; + + // Collect initial set of initializers + WorkList.reserve(ILE->getNumInits()); + auto CollectInitializers = [&](const InitListExpr *ILE) -> void { + if (ILE->getNumInits() == 0) + return; + // Add in reverse order so that when the worklist is processed + // the initializers are visited in order + for (size_t Index = ILE->getNumInits(); Index > 0; --Index) { + WorkList.push_back(std::make_tuple(ILE, ILE->getInit(Index - 1))); + } + }; + CollectInitializers(ILE); + + while (!WorkList.empty()) { + const Expr* Initializer; + const InitListExpr* ParentILE; + + std::tie(ParentILE, Initializer) = WorkList.back(); + WorkList.pop_back(); + if (const auto *SubILE = dyn_cast(Initializer)) { + CollectInitializers(SubILE); + continue; + } + // Field initializer of something that's not a union or struct + // (i.e. a leaf of "InitListExpr" graph). + if (Initializer->getType()->isPointerType()) { + handleUnsafeOperation(ParentILE, Initializer, + UnsafeOpKind::Assignment); + } + } + } + } + } + } + + // This handles visiting the children. We don't do that above because the + // visited Decls aren't necessarily `VarDecl`s (e.g. `TypedefDecl`) + VisitChildren(DS); +} + +void UnsafeOperationVisitor::VisitReturnStmt(const ReturnStmt *RS) { + const auto *const ReturnExpr = RS->getRetValue(); + if (ReturnExpr && ReturnExpr->getType()->isPointerType()) { + handleUnsafeOperation(RS, ReturnExpr, UnsafeOpKind::Return); + } + + // Visit children to look for other unsafe operations + VisitChildren(RS); +} + +void UnsafeOperationVisitor::VisitCallExpr(const CallExpr *CE) { + for (const auto &Arg : CE->arguments()) { + if (Arg->getType()->isPointerType()) { + handleUnsafeOperation(CE, Arg, UnsafeOpKind::CallArg); + } + } + + // Visit children to look for other unsafe operations + VisitChildren(CE); +} + +void UnsafeOperationVisitor::VisitCastExpr(const CastExpr *CE) { + if (CE->getSubExpr()->getType()->isPointerType()) { + handleUnsafeOperation(CE, CE->getSubExpr(), UnsafeOpKind::Cast); + } + + // Visit children to look for other unsafe operations + VisitChildren(CE); +} + +void clang::checkBoundsSafetySuggestions(const Decl *D, + BoundsSafetySuggestionHandler &Handler, + Sema &S) { + const Stmt *Body = D->getBody(); + assert(Body); + + // First pass: Populate DefMap by connecting variables + // to their potential values. + DefinitionVisitor DefV; + DefV.Visit(Body); + + // Second pass: Scan the function for unsafe operations. + // Use DefMap to assess where the operands are coming from. + // + // Fundamentally all of this could have been done in one pass, + // with some postprocessing, but it's nice to have some + // separation of concerns. + UnsafeOperationVisitor OpV(Handler, DefV.DefMap, S); + OpV.Visit(Body); + OpV.reset(); +} \ No newline at end of file diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 719c3a9312ec1..f091d10afbad7 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -15,10 +15,17 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins add_clang_library(clangSema AnalysisBasedWarnings.cpp + # TO_UPSTREAM(BoundsSafety) ON + BoundsSafetySuggestions.cpp + # TO_UPSTREAM(BoundsSafety) OFF CheckExprLifetime.cpp CodeCompleteConsumer.cpp DeclSpec.cpp DelayedDiagnostic.cpp + # TO_UPSTREAM(BoundsSafety) ON + DynamicCountPointerAssignmentAnalysis.cpp + DynamicCountPointerAssignmentAnalysisExported.cpp + # TO_UPSTREAM(BoundsSafety) OFF HLSLExternalSemaSource.cpp IdentifierResolver.cpp JumpDiagnostics.cpp diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp new file mode 100644 index 0000000000000..f78a6f897eb9d --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp @@ -0,0 +1,3667 @@ +//===--- DynamicCountPointerAssignmentAnalysis.cpp ------------------------===// +// +// 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 dynamic count pointer assignments +// for -fbounds-safety based on CFGBlock analysis. +// +//===----------------------------------------------------------------------===// + +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "clang/AST/Attr.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/Expr.h" +#include "clang/AST/IgnoreExpr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Sema/SemaInternal.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/SaveAndRestore.h" + +#include + +using namespace clang; + +namespace { +/// This is to generate a unique key for members in nested structs. This is +/// necessary to distinguish member accesses of nested structs in different +/// parent structs as well as members of same structs from different instances. +/// @code +/// struct nested { int i; }; +/// struct parent { struct nested n1; struct nested n2; }; +/// struct parent p1; +/// struct parent p2; +/// @endcode +/// For the above example, this function distinguishes member accesses "p1.n1.i" +/// and "p1.n2.i", and "p1.n1.i" and "p2.n1.i". +void computeMemberExprKey(const Expr *E, llvm::raw_string_ostream &OS, + bool Top = true) { + E = E->IgnoreParenCasts(); + if (auto ME = dyn_cast(E)) { + computeMemberExprKey(ME->getBase(), OS, false); + OS << (ME->isArrow() ? "->" : "."); + if (!Top) { + OS << ME->getMemberDecl()->getName(); + } + return; + } else if (auto DE = dyn_cast(E)) { + OS << DE->getDecl()->getDeclName(); + } else if (auto OVE = dyn_cast(E)) { + return computeMemberExprKey(OVE->getSourceExpr(), OS, Top); + } +} + +/// AssignedDeclRefResult - This represents result of analysis for a declaration +/// reference in LHS or RHS of variable assignment operation in terms of dynamic +/// counts or dynamic count pointers including params, locals, and fields. +/// @code +/// struct S { int *__counted_by(n) ptr; int n; }; +/// S s; +/// s.ptr = ptrVal; +/// s.n = intVal; +/// @endcode +/// For instance, this holds information whether "s.ptr" in the assignment +/// operation is dynamic count or dynamic count pointer and ditto for "s.n". +struct AssignedDeclRefResult { + /// The decl reference should be tracked. It is dynamic count or dynamic count + /// pointer. + unsigned IsTrackedVar : 1; + /// The decl reference is dynamic range pointer. + unsigned IsRangePtrVar : 1; + /// The decl reference is dynamic count. + unsigned IsCountVar : 1; + /// The decl reference is dynamic count pointer. + unsigned IsCountPtrVar : 1; + /// The dynamic count is out parameter. + unsigned IsOutCount : 1; + /// The dynamic count pointer is out parameter. + unsigned IsOutBuf : 1; + /// The decl is a single pointer to struct with flexible array member. + unsigned IsFlexBase : 1; + /// The decl is a nested struct containing a dynamic count decl + unsigned IsInnerStruct : 1; + unsigned IsCountForFam : 1; + /// The decl is a dependent param that is referred to by the return type that + /// is a bounds-attributed type. + unsigned IsDependentParamOfReturnType : 1; + /// Dereference level of decl reference. Dereference level of decl reference. + /// E.g., "**p = " has Level 2 while "p = " has zero. + unsigned Level : 32 - 10; + + /// The decl actually referenced in the analyzed expression. + ValueDecl *ThisVD = nullptr; + /// Partial key calculation of DeclGroup in which ThisVD belongs. + std::string Key; + /// Pointer to struct with fam as a key. + std::string FlexBaseKey; + /// Type of assignment expression. + QualType Ty; + /// Decl of struct containing flexible array member. + RecordDecl *FlexBaseDecl = nullptr; + /// Loc of decl reference expression. + SourceLocation Loc; + + AssignedDeclRefResult() { + IsTrackedVar = 0; + IsRangePtrVar = 0; + IsCountPtrVar = 0; + IsCountVar = 0; + IsOutCount = 0; + IsOutBuf = 0; + IsFlexBase = 0; + IsInnerStruct = 0; + IsCountForFam = 0; + IsDependentParamOfReturnType = 0; + Level = 0; + } +}; + +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast) { + SourceLocation Loc = PointerToCast->getBeginLoc(); + auto PtrTy = PointerToCast->getType()->getAs(); + auto SQT = PtrTy->getPointeeType().split(); + SQT.Ty = S.Context.CharTy.getTypePtr(); + QualType QualifiedChar = S.Context.getQualifiedType(SQT); + QualType CharPtrTy = S.Context.getPointerType( + QualifiedChar, PtrTy->getPointerAttributes()); + return S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(CharPtrTy), Loc, PointerToCast); +} + +/// getInnerType - This returns "Level" nested pointee type. +QualType getInnerType(QualType Ty, unsigned Level) { + for (unsigned i = 0; i < Level; ++i) { + if (!Ty->isPointerType()) + return QualType(); + Ty = Ty->getPointeeType(); + } + return Ty; +} + +bool isValueDeclOutBuf(const ValueDecl *VD) { + QualType Ty = VD->getType(); + return isa(VD) && Ty->isPointerType() && + Ty->getPointeeType()->isBoundsAttributedType(); +} + +bool isValueDeclOutCount(const ValueDecl *VD) { + const auto *Att = VD->getAttr(); + return isa(VD) && Att && Att->getIsDeref(); +} + +bool isSinglePtrToStructWithFam(Sema &S, QualType Ty) { + if (!Ty->isSinglePointerType() || Ty->isBoundsAttributedType()) + return false; + + FlexibleArrayMemberUtils FlexUtils(S); + return FlexUtils.GetFlexibleRecord(Ty->getPointeeType()); +} + +/// analyzeAssignedDeclCommon - This shares analysis logic for declaration +/// reference from assignment expression and variable declaration statement. +void analyzeAssignedDeclCommon(Sema &S, ValueDecl *VD, QualType Ty, + AssignedDeclRefResult &Result) { + if (Ty->isCountAttributedType()) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsCountPtrVar = 1; + return; + } + + if (Result.Level == 0 && isa(VD) && Ty->isPointerType() && + Ty->getPointeeType()->isCountAttributedType()) { + Result.IsOutBuf = 1; + return; + } + + const auto *Attr = VD->getAttr(); + + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + bool IsDepParamOfRetType = VD->isDependentParamOfReturnType(&RetType, &Info); + if (IsDepParamOfRetType) { + Result.IsDependentParamOfReturnType = 1; + Result.ThisVD = VD; + } + + bool IsDependentCount = + Attr || (IsDepParamOfRetType && isa(RetType)); + if (IsDependentCount) { + bool IsDeref = Attr ? Attr->getIsDeref() : Info->isDeref(); + if (Result.Level == 0 && isa(VD) && IsDeref) { + Result.IsOutCount = 1; + } else if (Result.Level < 2) { + // If the VD doesn't have a DependerDeclsAttr, this VD is used only in the + // return type. For example: + // int *__counted_by(count) foo(int count); + // In that case, the VD can be freely updated and we won't generate a + // bounds-check for the updates, so set IsTrackedVar to false. + Result.IsTrackedVar = !!Attr; + Result.ThisVD = VD; + if (Ty->isIntegralOrEnumerationType()) + Result.IsCountVar = 1; + else + Result.IsInnerStruct = 1; + } else + llvm_unreachable( + "Dependent count reference can only be a single-level indirection"); + return; + } + + if (Ty->isDynamicRangePointerType()) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsRangePtrVar = 1; + return; + } + + if (isSinglePtrToStructWithFam(S, Ty)) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsFlexBase = 1; + return; + } +} + +/// analyzeVarDecl - This function analyzes variable declaration like +/// "int *__counted_by(len) ptr;" or "int len;". +void analyzeVarDecl(Sema &S, VarDecl *Var, AssignedDeclRefResult &Result) { + Result.Ty = Var->getType(); + Result.Loc = Var->getLocation(); + analyzeAssignedDeclCommon(S, Var, Result.Ty, Result); +} + +/// Check whether the dep group contains a flexible array member, +/// and update FlexBaseKey and FlexBaseDecl if so, for matching +/// with the relevant DepGroup. +/// Because counted_by pointers can share count fields with the +/// FAM, this relation can be indirect. +/// In the code below, assigning to \c SharedCount::p requires assigning +/// to \c SharedCount::len, which in turns requires assigning to the +/// flexbase pointer from a wide pointer: +/// \code +/// struct SharedCount { +/// int * __counted_by(len) p; +/// int len; +/// int fam[__counted_by(len)]; +/// }; +/// \endcode +/// +/// Fields in structs that are nested in other structs can be referred +/// to from fields in the outer structs. Care must be taken to only check +/// the dependent fields when the inner struct type is actually used in the +/// context of the outer struct. This is checked with \c isParentStructOf(). +/// Example of irrelevant dep decl ( \c SimpleOuter1::fam): +/// \code +/// struct SimpleInner { +/// int dummy; +/// int len; +/// }; +/// struct SimpleOuter1 { +/// struct SimpleInner hdr; +/// char fam[__counted_by(hdr.len)]; +/// }; +/// void set_len(struct SimpleInner * p) { +/// // struct SimpleInner doesn't contain any FAMs, so not checked +/// // -fbounds-safety doesn't allow taking address of +/// // \c SimpleOuter1::hdr though, so this is never referred to by +/// // a FAM and thus safe +/// p->len = 2; +/// } +/// struct SimpleOuter2 { +/// struct SimpleInner hdr; +/// char fam[__counted_by(hdr.len)]; +/// }; +/// struct SimpleOuter2 *foo(int len) { +/// // This is a flex base pointer, but \c SimpleOuter1::fam should not be +/// // included in the analysis in this context since it's the +/// struct SimpleOuter2 *p = malloc(sizeof(struct SimpleOuter2) + len); +/// p->hdr.len = len; +/// return p; +/// } +/// \endcode +/// +/// Even if a pointer doesn't directly share a count variable with a FAM, +/// it may belong to the same group indirectly as seen in \c p1 below: +/// \code +/// struct S { +/// int len1; +/// int len2; +/// int * __counted_by(len1) p1; +/// int * __counted_by(len1 + len2) p2; +/// int fam[__counted_by(len2)]; +/// }; +/// \endcode +void analyzeFlexBase(Sema &S, ValueDecl *VD, Expr *E, + AssignedDeclRefResult &Result) { + std::string FlexBase; + llvm::raw_string_ostream FlexBaseOS(FlexBase); + RecordDecl *FlexBaseDecl = + DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey(E, &FlexBaseOS); + if (!FlexBaseDecl) + return; + + // Recursion is needed to check indirect flexbase members, so we need to track + // which decls we've already visited. + llvm::SmallPtrSet Visited; + std::function CheckDependentDecls; + CheckDependentDecls = [&Visited, &CheckDependentDecls, &FlexBaseDecl, + &FlexBase, &Result](const ValueDecl *VD) { + if (!FlexBaseDecl->isParentStructOf(VD)) + return false; // if this is a nested field referred to by outer structs, + // only consider fields in the scope of the relevant struct. + if (!Visited.insert(VD).second) + return false; + + if (VD->getType()->isIncompleteArrayType() && + VD->getType()->isCountAttributedType()) { + Result.FlexBaseKey = FlexBase; + Result.FlexBaseDecl = FlexBaseDecl; + return true; + } + + if (auto *Att = VD->getAttr()) + for (const auto *D : Att->dependerDecls()) + if (CheckDependentDecls(cast(D))) + return true; + + const auto *DCPTy = VD->getType()->getAs(); + if (!DCPTy && VD->getType()->isPointerType()) + DCPTy = VD->getType()->getPointeeType()->getAs(); + + if (!DCPTy) + return false; + + for (const auto &DI : DCPTy->dependent_decls()) + if (CheckDependentDecls(cast(DI.getDecl()))) + return true; + return false; + }; + + if (auto *Att = VD->getAttr()) { + // CheckDependentDecls searches depth first, so check all the direct + // neighbors first to correctly set the IsCountForFam. If a __counted_by + // pointer shares count with a FAM it should belong to the same group and + // thus requires a FlexBaseKey. But it is not the count, and any count + // variables not shared should not be treated as count for FAM, despite + // having FlexBaseKey. + for (const auto *D : Att->dependerDecls()) { + const auto *DepVD = cast(D); + if (FlexBaseDecl->isParentStructOf(DepVD) && + DepVD->getType()->isIncompleteArrayType() && + DepVD->getType()->isCountAttributedType()) { + Result.FlexBaseKey = FlexBase; + Result.FlexBaseDecl = FlexBaseDecl; + Result.IsCountForFam = true; + return; + } + } + } + + // Check if this is a pointer that shares a count with a FAM. + CheckDependentDecls(VD); +} + +/// analyzeAssignedDeclRef - This function analyzes declaration reference in +/// assignment expression, including DeclRefExpr and MemberExpr. This also +/// supports analysis of dereferenced decl like "*p". +void analyzeAssignedDeclRef(Sema &S, Expr *E, AssignedDeclRefResult &Result) { + E = E->IgnoreParenCasts(); + if (auto UO = dyn_cast(E)) { + /// This is to support cases like '*&*ptr = ...'. + switch (UO->getOpcode()) { + case UO_Deref: + Result.Level++; + break; + case UO_AddrOf: + /// '&x' would be non-assignable'. + if (Result.Level == 0) + return; + Result.Level--; + break; + default: + return; + } + return analyzeAssignedDeclRef(S, UO->getSubExpr(), Result); + } + + ValueDecl *VD = nullptr; + std::string Prefix; + if (auto ME = dyn_cast(E)) { + llvm::raw_string_ostream PrefixOS(Prefix); + computeMemberExprKey(ME, PrefixOS); + VD = ME->getMemberDecl(); + analyzeFlexBase(S, VD, ME, Result); + } else if (auto DRE = dyn_cast(E)) { + VD = DRE->getDecl(); + } + if (!VD) + return; + + QualType Ty = getInnerType(E->getType(), Result.Level); + if (Ty.isNull()) + return; + + Result.Ty = E->getType(); + Result.Loc = E->getExprLoc(); + Result.Key = Prefix; + + analyzeAssignedDeclCommon(S, VD, Ty, Result); +} + +bool ReplaceSubExpr(ParentMap &PM, Expr *SubExpr, Expr *NewSubExpr) { + Stmt *Parent = PM.getParent(SubExpr); + if (!Parent) + return false; + + for (auto &C : Parent->children()) { + if (C == SubExpr) { + *&C = NewSubExpr; + PM.setParent(NewSubExpr, Parent); + PM.setParent(SubExpr, NewSubExpr); + return true; + } + } + return false; +} + +/// Build the bounds check expression for delayed count/range checks. +class PreAssignCheck { + Sema &SemaRef; + ParentMap &PM; + Expr *GuardedValue; + +public: + using DeclToNewValueTy = llvm::DenseMap>; + +private: + ReplaceDeclRefWithRHS DeclReplacer; + +public: + PreAssignCheck(Sema &SemaRef, ParentMap &PM, Expr *GuardedValue, + DeclToNewValueTy &DeclToNewValue) + : SemaRef(SemaRef), + PM(PM), + GuardedValue(GuardedValue), + DeclReplacer(SemaRef, DeclToNewValue) {} + +private: + ExprResult createZeroValue(Sema &S, QualType Ty) { + auto &Ctx = SemaRef.Context; + auto BitWidth = Ctx.getTypeSize(Ctx.IntTy); + llvm::APInt ZeroVal(BitWidth, 0); + ExprResult Zero = IntegerLiteral::Create(SemaRef.Context, ZeroVal, Ctx.IntTy, + SourceLocation()); + CastKind CK = CastKind::CK_NoOp; + auto ConversionType = S.CheckAssignmentConstraints(Ty, Zero, CK, true); + if (ConversionType == Sema::AssignConvertType::Incompatible) { + // `CK` is not set for this return value so bail out. + return ExprError(); + } + + switch (CK) { + case CK_NoOp: + return Zero.get(); + case CK_IntegralToPointer: + CK = CK_NullToPointer; + LLVM_FALLTHROUGH; + default: + return S.ImpCastExprToType(Zero.get(), Ty, CK); + } + } + +public: + + using TypeExprPairTy = std::pair; + + ExprResult getMaterializedValueIfNot( + Expr *E, SmallVectorImpl *MateredExprs = nullptr, + bool UpdateParent = true) { + if (isa(E)) + return E; + + if (isa(E)) + return createZeroValue(SemaRef, E->getType()); + + OpaqueValueExpr *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E); + if (MateredExprs) { + MateredExprs->push_back(OVE); + } + if (UpdateParent) + ReplaceSubExpr(PM, E, OVE); + return OVE; + } + + void insertDeclToNewValue(const ValueDecl *VD, Expr *New, unsigned Level = 0) { + DeclReplacer.DeclToNewValue[VD] = std::make_pair(New, Level); + } + + void setMemberBase(Expr *Base) { + DeclReplacer.MemberBase = Base; + } + + // UpFront -> Materialize something + // MateredExprsInFlight is not... + // We need InitListExpr to be in UpFront! + ExprResult build(SmallVectorImpl &Pairs, InitListExpr *IL, + SmallVectorImpl &MateredExprs) { + Expr *WrappedExpr = IL; + // Reverse so that the first check is inserted at the outermost. + for (auto I = Pairs.rbegin(), E = Pairs.rend(); I != E; ++I) { + auto Pair = *I; + ExprResult R; + SmallVectorImpl *MateredExprsPtr = + (Pair == Pairs.front()) ? &MateredExprs : nullptr; + if (const auto *DCPTy = Pair.first->getAs()) { + R = build(DCPTy, Pair.second, WrappedExpr, MateredExprsPtr); + } else { + const auto *DRPTy = Pair.first->getAs(); + assert(DRPTy); + R = build(DRPTy, Pair.second, WrappedExpr, MateredExprsPtr); + } + if (R.isInvalid()) + return ExprError(); + WrappedExpr = R.get(); + } + return WrappedExpr; + } + + ExprResult build(QualType Ty, Expr *WidePtr, Expr *WrappedValue = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + if (const auto *DCPTy = Ty->getAs()) { + return build(DCPTy, WidePtr, WrappedValue, MateredExprs); + } else { + const auto *DRPTy = Ty->getAs(); + assert(DRPTy); + return build(DRPTy, WidePtr, WrappedValue, MateredExprs); + } + } + + /// This is to build the trap condition necessary for a sized_by or counted_by pointer and its related + /// count assignments like: Count = NewCount; Ptr = WidePtr; + /// + /// The condition to check will look like below: + /// \code + /// 0 <= NewCount && (lower(WidePtr) <= WidePtr <= upper(WidePtr)) && + /// NewCount <= upper(WidePtr) - WidePtr + /// \endcode + /// The reason why we do `NewCount <= upper(WidePtr) - WidePtr` instead of + /// `WidePtr + NewCount <= upper(WidePtr)` is that the backend optimizes the former better because + /// for the latter the compiler needs to consider integer overflow on the pointer arithmetic. + /// For `upper(WidePtr) - WidePtr`, on the other hand, we can assume no overflow is happening because + /// we already checked `WidePtr <= upper(WidePtr)`. + ExprResult build(const CountAttributedType *Ty, Expr *WidePtr, + Expr *WrappedValue = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + if (!WrappedValue) + WrappedValue = GuardedValue; + auto &Ctx = SemaRef.Context; + + SmallVector CommonExprs; + if (!MateredExprs) { + MateredExprs = &CommonExprs; + } + + assert(!Ty->getCountExpr()->HasSideEffects(Ctx)); + ExprResult CountR = + DeclReplacer.TransformBoundsAttrExpr(Ty->getCountExpr()); + if (CountR.isInvalid()) + return ExprError(); + Expr *Count = CountR.get(); + assert(!Count->HasSideEffects(Ctx)); + + SourceLocation Loc = WrappedValue->getBeginLoc(); + // Later, we might be able to merge diagnostics to check if count is + // non-constant when a pointer is null here instead of doing it separately. + if (isa(WidePtr->IgnoreImpCasts()) || + WidePtr->isNullPointerConstantIgnoreCastsAndOVEs( + SemaRef.getASTContext(), Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + if (Ty->isOrNull()) { + if (MateredExprs->empty()) + return WrappedValue; + // Materialize the OVEs, but perform the assignment unconditionally. + // This materialization happens as part of BoundsCheckExpr otherwise. + auto Result = MaterializeSequenceExpr::Create( + SemaRef.Context, WrappedValue, *MateredExprs); + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result, + *MateredExprs, true); + return Result; + } + // if wideptr == 0 then count expr should also be 0. + ExprResult Zero = createZeroValue(SemaRef, Count->getType()); + if (!Zero.get()) + return ExprError(); + ExprResult Cond = SemaRef.CreateBuiltinBinOp(Loc, BO_EQ, Count, + Zero.get()); + if (Cond.isInvalid()) + return ExprError(); + return SemaRef.BuildBoundsCheckExpr(WrappedValue, Cond.get(), *MateredExprs); + } + + ExprResult MatWidePtr = getMaterializedValueIfNot(WidePtr, MateredExprs); + if (!(WidePtr = MatWidePtr.get())) + return ExprError(); + + SmallVector WidePtrSeq; + ExprResult LowerR = SemaRef.BuildLowerBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (LowerR.isInvalid()) + return ExprError(); + + WidePtrSeq.push_back(LowerR.get()); + WidePtrSeq.push_back(WidePtr); + + ExprResult UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (UpperR.isInvalid()) + return ExprError(); + + WidePtrSeq.push_back(UpperR.get()); + + ExprResult WidePtrSeqR = BoundsCheckBuilder::BuildLEChecks(SemaRef, + WrappedValue->getExprLoc(), + WidePtrSeq, *MateredExprs); + if (WidePtrSeqR.isInvalid()) + return ExprError(); + + // 0 <= Count + SmallVector CountCheckSeq; + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + auto Zero = createZeroValue(SemaRef, Count->getType()); + if (!Zero.get()) + return ExprError(); + CountCheckSeq.push_back(Zero.get()); + } + + CountCheckSeq.push_back(Count); + + UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*ToSingle*/true); + if (UpperR.isInvalid()) + return ExprError(); + + ExprResult WidePtrCmpOperand = WidePtr; + if (Ty->isCountInBytes()) { + UpperR = CastToCharPointer(SemaRef, UpperR.get()); + WidePtrCmpOperand = CastToCharPointer(SemaRef, WidePtrCmpOperand.get()); + if (WidePtrCmpOperand.isInvalid()) + return ExprError(); + } else { + // We can't do pointer arithmetic on incomplete pointee types. The + // `UpperMinusPtr` computation will fail this case and emit a confusing + // `err_typecheck_arithmetic_incomplete_or_sizeless_type` diagnostic. To + // avoid this bail out early. + if (UpperR.get()->getType()->getPointeeType()->isIncompleteType() || + WidePtrCmpOperand.get() + ->getType() + ->getPointeeType() + ->isIncompleteType()) + return ExprError(); + } + // Upper - Ptr + ExprResult UpperMinusPtr = SemaRef.CreateBuiltinBinOp(Loc, BO_Sub, UpperR.get(), + WidePtrCmpOperand.get()); + + if (UpperMinusPtr.isInvalid()) + return ExprError(); + + CountCheckSeq.push_back(UpperMinusPtr.get()); + // 0 <= Count <= (Upper - Ptr) + ExprResult CountCheckSeqR = BoundsCheckBuilder::BuildLEChecks(SemaRef, + Loc, + CountCheckSeq, *MateredExprs); + if (CountCheckSeqR.isInvalid()) + return ExprError(); + + if (Ty->isOrNull()) { + ExprResult NullCheck = + SemaRef.CreateBuiltinUnaryOp(Loc, UO_LNot, WidePtr); + if (NullCheck.isInvalid()) + return ExprError(); + // !Ptr || 0 <= Count <= (Upper - Ptr) + CountCheckSeqR = SemaRef.CreateBuiltinBinOp(Loc, BO_LOr, NullCheck.get(), + CountCheckSeqR.get()); + if (CountCheckSeqR.isInvalid()) + return ExprError(); + } + + // __counted_by/__sized_by: + // (Lower <= Ptr <= Upper) && (0 <= Count <= Upper - Ptr) + // __counted_by_or_null/__sized_by_or_null: + // (Lower <= Ptr <= Upper) && (!Ptr || (0 <= Count <= Upper - Ptr)) + // This allows null pointers to be assigned to *_or_null variables + // even if the count is negative or the count is larger than the range of + // the RHS (null pointers generally have 0 range). Assigning from one + // *_or_null variable to another works because the RHS is first converted to + // a 0 range __bidi_indexable if the value is null, so it still passes the + // range check. We preserve the semantics of __bidi_indexables to + // trap if OOB when converted to a type that cannot represent the OOB-ness. + // So the wide pointer (0x0, 0xBAD, 0x00B) will trap when assigned to a + // *_or_null pointer even though the base pointer is null, because we cannot + // represent the lower bound in the LHS. + ExprResult CondSeqR = SemaRef.CreateBuiltinBinOp( + Loc, BO_LAnd, WidePtrSeqR.get(), CountCheckSeqR.get()); + if (CondSeqR.isInvalid()) + return ExprError(); + + return SemaRef.BuildBoundsCheckExpr(WrappedValue, CondSeqR.get(), + *MateredExprs); + } + + /// This is to build sequential checks, `lower(WidePtr) <= start <= WidePtr <= end <= upper(WidePtr)`. + ExprResult build(const DynamicRangePointerType *Ty, Expr *WidePtr, + Expr *WrappedExpr = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + assert(!WidePtr->HasSideEffects(SemaRef.Context) || + isa(WidePtr) || WidePtr->containsErrors()); + if (!WrappedExpr) + WrappedExpr = GuardedValue; + SmallVector Bounds; + SmallVector CommonExprs; + SourceLocation Loc = WrappedExpr->getBeginLoc(); + + if (!MateredExprs) + MateredExprs = &CommonExprs; + auto &Ctx = SemaRef.Context; + if (isa(WidePtr->IgnoreImpCasts())) { + llvm::APInt Zero(Ctx.getTypeSize(WidePtr->getType()), 0); + WidePtr = IntegerLiteral::Create(SemaRef.Context, Zero, Ctx.getIntPtrType(), Loc); + + ExprResult ImpResult = SemaRef.BuildCStyleCastExpr( + Loc, Ctx.getTrivialTypeSourceInfo(QualType(Ty, 0)), Loc, WidePtr); + if (ImpResult.isInvalid()) + return ExprError(); + WidePtr = ImpResult.get(); + } + + ExprResult MatWidePtr = getMaterializedValueIfNot(WidePtr, MateredExprs); + if (!(WidePtr = MatWidePtr.get())) + return ExprError(); + + auto PushValidOrErr = [&](ExprResult Result) -> bool { + if (Expr *RE = Result.get()) { + if (auto *Lit = dyn_cast(RE)) { + // This is only possible if TransformExpr transformed an implicit + // null-to-pointer cast. + assert(Lit->getValue().isZero()); + Result = SemaRef.ImpCastExprToType(Lit, WidePtr->getType(), + CK_NullToPointer); + if (!(RE = Result.get())) + return false; + } + Bounds.push_back(RE); + return true; + } + return false; + }; + + if (WidePtr->getType()->isBidiIndexablePointerType()) { + ExprResult LowerR = SemaRef.BuildLowerBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (LowerR.isInvalid()) + return ExprError(); + Bounds.push_back(LowerR.get()); + } + + if (!PushValidOrErr(WidePtr)) + return ExprError(); + + // The pointer that has an associated end pointer is responsible to do the + // bounds checks: + // lb(new_ptr) <= start <= new_ptr <= end <= ub(new_ptr) + // Thus, we check if the pointer has an end pointer. This is consistent with + // how we handle assignments (and this is a routine we handle init lists). + // XXX: This is based on the assumption that only '__ended_by' is exposed to + // the users and '__started_by' is an implicit attribute the compiler adds + // based on the corresponding + // '__ended_by'. We may revisit this if we decide to expose '__started_by' + // to users. + if (auto *End = Ty->getEndPointer()) { + if (!PushValidOrErr(DeclReplacer.TransformBoundsAttrExpr(End))) + return ExprError(); + } + + ExprResult UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (UpperR.isInvalid()) + return ExprError(); + Bounds.push_back(UpperR.get()); + + ExprResult Cond = + BoundsCheckBuilder::BuildLEChecks(SemaRef, + Loc, + Bounds, *MateredExprs); + + if (Cond.isInvalid()) + return ExprError(); + return SemaRef.BuildBoundsCheckExpr(WrappedExpr, Cond.get(), *MateredExprs); + } + + void buildAndChain(Expr *AssignExpr, Expr *WidePtr) { + Expr *LHS = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + LHS = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + LHS = UO->getSubExpr(); + } + + LHS = LHS->IgnoreParenCasts(); + if (auto *OVE = dyn_cast(LHS)) { + LHS = OVE->getSourceExpr()->IgnoreParenCasts(); + } + + if (auto *ME = dyn_cast(LHS)) { + setMemberBase(ME->getBase()); + } + + buildAndChain(AssignExpr->getType(), WidePtr); + } + + void buildAndChain(QualType Ty, Expr *WidePtr) { + if (!Ty->isBoundsAttributedType()) + return; + + auto *CE = dyn_cast(WidePtr); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast) + WidePtr = CE->getSubExpr(); + ExprResult WidePtrR = ForceRebuild(SemaRef).TransformExpr(WidePtr); + assert(!WidePtrR.isInvalid()); + WidePtr = WidePtrR.get(); + + // This is a good place to modify AssignOp if we need to. Check if any work + // needs to be done due to dynamic-bound pointer types. + ExprResult Result; + if (const auto *DBPTy = Ty->getAs()) { + if (const auto *DCPTy = dyn_cast(DBPTy)) { + Result = build(DCPTy, WidePtr); + } else { + const auto *DRPTy = dyn_cast(DBPTy); + if (DRPTy->getEndPointer()) + Result = build(DRPTy, WidePtr); + } + } + + // The error should have been diagnosed somewhere else. + if (Result.get()) + GuardedValue = Result.get(); + } + + /// The function generates an assignment check for flexible base similar to + /// pre-assignment checks on count and pointer pairs. + /// + /// @code + /// f = new_f; // check if `0 <= new_count <= bounds(new_f)` + /// f->count = new_count; + /// @endcode + void buildAndChainFlex(Expr *WidePtr) { + llvm::SmallVector OVEs; + auto *CE = dyn_cast(WidePtr); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast && + !WidePtr->getType()->isPointerTypeWithBounds()) + WidePtr = CE->getSubExpr(); + Expr *OldWidePtr = WidePtr; + ExprResult WidePtrR = ForceRebuild(SemaRef).TransformExpr(WidePtr); + assert(!WidePtrR.isInvalid()); + WidePtr = OpaqueValueExpr::EnsureWrapped( + SemaRef.Context, WidePtrR.get(), OVEs); + + SourceLocation Loc = GuardedValue->getBeginLoc(); + + // This is a good place to modify AssignOp if we need to. Check if any work + // needs to be done due to dynamic-bound pointer types. + CopyExpr Copy(SemaRef); + for (auto It : DeclReplacer.DeclToNewValue) + Copy.UnsafelyAddDeclSubstitution(const_cast(It.first), + It.second.first->IgnoreImpCasts()); + ExprResult Result = + BoundsCheckBuilder::CheckFlexibleArrayMemberSizeWithOVEs( + SemaRef, Loc, BoundsCheckKind::FlexibleArrayCountAssign, WidePtr, + OVEs, &Copy); + if (Result.isInvalid()) + return; + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result.get(), OVEs); + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result.get(), OVEs, true); + } + + bool Succ = ReplaceSubExpr(PM, OldWidePtr, Result.get()); + (void)Succ; + assert(Succ); + } + + std::tuple GetFlexBase(Expr *InE) { + auto *ME = cast(InE); + QualType RecordTy = ME->getBase()->getType(); + if (ME->isArrow()) + RecordTy = RecordTy->getPointeeType(); + auto *RD = RecordTy->getAsRecordDecl(); + assert(RD); + if (!RD->hasFlexibleArrayMember()) + return GetFlexBase(ME->getBase()); + return {ME->getBase(), ME->isArrow(), RD}; + } + + /// This function generates a code to assert that the new count is + /// smaller or equal to the original count value. + /// + /// @code + /// typedef struct { int count; char fam[__counted_by(count)]; } flex_t; + /// flex_t g_flex = ... + /// + /// void test() { + /// g_flex.count = new_count; // check if `0 <= new_count <= g_flex.count` + /// } + /// @endcode + void buildAndChainOldCountCheck(Expr *AssignExpr) { + Expr *OldSelf = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + OldSelf = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + OldSelf = UO->getSubExpr(); + } + OldSelf = OldSelf->IgnoreParenCasts(); + if (auto *OVE = dyn_cast(OldSelf)) + OldSelf = OVE->getSourceExpr()->IgnoreParenCasts(); + // Currently, this can only be reached with a member expression, + // which is the count of flexible array member. + + Expr *Base; + bool IsArrow; + RecordDecl *RD; + // The member expression may be nested, recurse to the flexible array + // struct. + std::tie(Base, IsArrow, RD) = GetFlexBase(OldSelf); + + FieldDecl *LastField; + for (auto *FD : RD->fields()) + LastField = FD; + assert(LastField->getType()->isIncompleteArrayType()); + const auto *DCPTy = LastField->getType()->getAs(); + assert(DCPTy); + Expr *Count = DCPTy->getCountExpr(); + + SmallVector CommonExprs; + ExprResult OpaqueBase; + if (auto *OVE = dyn_cast(Base)) { + OpaqueBase = OVE; + } else { + OpaqueBase = getMaterializedValueIfNot(Base, &CommonExprs); + if (OpaqueBase.isInvalid()) + return; + } + + ExprResult OldCount = + SemaRef.InstantiateDeclRefField(OpaqueBase.get(), IsArrow, Count); + + SmallVector Counts; + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + ExprResult Zero = createZeroValue(SemaRef, Count->getType()); + if (Zero.isInvalid()) + return; + Counts.push_back(Zero.get()); + } + + ExprResult NewCount = DeclReplacer.TransformBoundsAttrExpr(Count); + NewCount = SemaRef.DefaultLvalueConversion(NewCount.get()); + if (NewCount.isInvalid()) + return; + Counts.push_back(NewCount.get()); + + OldCount = SemaRef.DefaultLvalueConversion(OldCount.get()); + if (OldCount.isInvalid()) + return; + Counts.push_back(OldCount.get()); + + ExprResult LEChecks = BoundsCheckBuilder::BuildLEChecks( + SemaRef, GuardedValue->getExprLoc(), Counts, CommonExprs); + // Error should have been handled inside 'BuildLEChecks'. + if (LEChecks.isInvalid()) + return; + ExprResult Result = + SemaRef.BuildBoundsCheckExpr(GuardedValue, LEChecks.get(), CommonExprs); + if (Result.isInvalid()) + return; + GuardedValue = Result.get(); + } + + /// Perform if the new assignment is within the old range when the rest of the dependent pointers will not be changed. + bool buildAndChainOldRangeCheck(Expr *AssignExpr, Expr *NewSelf) { + const auto *DRPTy = AssignExpr->getType()->getAs(); + assert(DRPTy && (DRPTy->getStartPointer() || DRPTy->getEndPointer())); + SmallVector Ptrs; + Expr *OldSelf = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + OldSelf = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + OldSelf = UO->getSubExpr(); + } + + // The new pointer might be still within the old range, but the buffer has possibly + // been resized its bounds, e.g., by calling realloc(). For such cases, we fallback + // to generic range checks. + if (NewSelf->HasSideEffects(SemaRef.Context)) + return false; + + // old_start <= new_ptr_val <= old_end + auto rebuildAndPushPtr = [&](Expr *RangePtr, Expr *OldSelf = nullptr) { + if (!RangePtr) { + assert(OldSelf && !OldSelf->HasSideEffects(SemaRef.Context)); + ExprResult OldSelfR = ForceRebuild(SemaRef).TransformExpr(OldSelf); + OldSelfR = SemaRef.DefaultFunctionArrayLvalueConversion(OldSelfR.get()); + assert(!OldSelfR.isInvalid()); + Ptrs.push_back(OldSelfR.get()); + return; + } + + assert(!RangePtr->HasSideEffects(SemaRef.Context)); + // Since we materialize self assignments, we can reuse the materialized value. + ExprResult NewRangePtr = DeclReplacer.TransformBoundsAttrExpr(RangePtr); + assert(!NewRangePtr.isInvalid()); + // This is assuming the range has not been changed. + NewRangePtr = SemaRef.DefaultFunctionArrayLvalueConversion(NewRangePtr.get()); + assert(!NewRangePtr.isInvalid()); + Ptrs.push_back(NewRangePtr.get()); + }; + + rebuildAndPushPtr(DRPTy->getStartPointer(), OldSelf); + rebuildAndPushPtr(nullptr, NewSelf); + rebuildAndPushPtr(DRPTy->getEndPointer(), OldSelf); + + SmallVector CommonExprs; + ExprResult LEChecks = BoundsCheckBuilder::BuildLEChecks(SemaRef, + GuardedValue->getExprLoc(), + Ptrs, CommonExprs); + assert(!LEChecks.isInvalid()); + ExprResult Result = SemaRef.BuildBoundsCheckExpr(GuardedValue, + LEChecks.get(), + CommonExprs); + assert(!Result.isInvalid()); + GuardedValue = Result.get(); + return true; + } + + Expr *getChainedBoundsCheckExpr() const { + return GuardedValue; + } + +}; + +class DeclRefFinder : public ConstEvaluatedExprVisitor { +public: + typedef ConstEvaluatedExprVisitor Inherited; + const Decl *DeclToFind; + const Expr *Result; + + DeclRefFinder(ASTContext &Ctx, const Expr *Haystack, const Decl *Needle) + : Inherited(Ctx), DeclToFind(Needle) { + Visit(Haystack); + } + + const Expr *getExpr() { return Result; } + + void VisitMemberExpr(const MemberExpr *E) { + if (E->getMemberDecl() == DeclToFind) { + Result = E; + } + } + + void VisitDeclRefExpr(const DeclRefExpr *E) { + if (E->getDecl() == DeclToFind) { + Result = E; + } + } +}; + +class CountDepGroup; +class RangeDepGroup; + +/// Group decls with the decl referenced in count expression as a key decl. +/// Pointers that share the same count decl are in the same group. We currently +/// have "a" key decl because we don't currently allow having multiple count +/// variables referenced in a count expression. +/// FIXME: This should be extended to have multiple count declarations. +/// rdar://70692513 +class DepGroup { +public: + enum DepGroupKind { DGK_Count, DGK_Range }; +private: + const DepGroupKind Kind; + +protected: + class AssignExprInfo { + llvm::PointerIntPair Data; + llvm::PointerIntPair ExtData; + // The map 'ReferencedDecls' tracks dependent Decls referenced inside the assignment + // expression and the nested level of the decl reference, for the sake of diagnostics. + llvm::DenseMap ReferencedDecls; + enum { + SELF_ASSIGN = 0, + COUNT_VAR = 1, + COUNT_FOR_FAM = 0, + FLEX_BASE = 1, + }; + + public: + explicit AssignExprInfo(Expr *E, ValueDecl *VD, bool SelfAssign, + bool CountVar, bool CountForFam, bool FlexBase) + : Data(E, ((SelfAssign ? 1 << SELF_ASSIGN : 0) | + (CountVar ? 1 << COUNT_VAR : 0))), + ExtData(VD, ((CountForFam ? 1 << COUNT_FOR_FAM : 0) | + (FlexBase ? 1 << FLEX_BASE : 0))) {} + + bool IsSelfAssign() const { return (Data.getInt() >> SELF_ASSIGN) & 1; } + bool IsCountVar() const { return (Data.getInt() >> COUNT_VAR) & 1; } + bool IsCountForFam() const { + return (ExtData.getInt() >> COUNT_FOR_FAM) & 1; + } + bool IsFlexBase() const { return (ExtData.getInt() >> FLEX_BASE) & 1; } + void AddReferencedDecl(const ValueDecl *VD, unsigned Level) { + ReferencedDecls.insert(std::make_pair(VD, Level)); + } + const llvm::DenseMap& getReferencedDecls() const { + return ReferencedDecls; + } + + bool IsDeclInit() const { return !getExpr() && getDeclInit(); } + ValueDecl *getValueDecl() const { return ExtData.getPointer(); } + Expr *getExpr() const { return Data.getPointer(); } + Expr *getDeclInit() const { + auto Var = dyn_cast(getValueDecl()); + if (!Var) + return nullptr; + return Var->getInit(); + } + Expr *getAssignOrInitExpr() const { + return IsDeclInit() ? getDeclInit() : getExpr(); + } + }; + +protected: + bool SideEffectAfter = false; + bool SkipFlexCheck = false; + bool FlexBaseNull = false; + std::string Key; + std::string FlexBaseKey; + /// FIXME: Support multiple dependent lengths in a length expression. + llvm::SmallPtrSet KeyDecls; + // The map 'AssignDecls' keeps a link to the expression that assigns to + // the decl for diagnostics, i.e., the 'unsigned' data is an index to + // 'AssignExprList'. + llvm::DenseMap AssignedDecls; + llvm::DenseMap> DeclToNewValue; + llvm::SmallPtrSet SelfAssignedDecls; + llvm::SmallVector AssignExprList; + llvm::SmallVector MateredExprsUpFront; + + std::string getPrefixedName(const ValueDecl *VD, unsigned NestedLevel = 0) const { + std::string starPrefix = ""; + while (NestedLevel--) + starPrefix += "*"; + return (starPrefix + Key + VD->getName()).str(); + } + + std::string getPrefixedNameWithFlexBase(const Expr *E, + unsigned NestedLevel = 0) const { + std::string starPrefix = ""; + while (NestedLevel--) + starPrefix += "*"; + std::string MemberExprString; + llvm::raw_string_ostream MemberExprStringOS(MemberExprString); + computeMemberExprKey(E, MemberExprStringOS, /*Top*/ false); + return starPrefix + FlexBaseKey + MemberExprString; + } + + bool isFlexBasePointer() const { + size_t KeyLen = FlexBaseKey.length(); + return KeyLen > 2 && FlexBaseKey.substr(KeyLen - 2, 2) == "->"; + } + + bool hasAssignToFlexBase() const { + return AssignExprList.back().IsFlexBase(); + } + + bool hasFlexBase() const { return !FlexBaseKey.empty(); } + + std::string getFlexBaseName() const { + if (FlexBaseKey.empty()) + return ""; + size_t KeyLen = FlexBaseKey.length(); + if (isFlexBasePointer()) + return FlexBaseKey.substr(0, KeyLen - 2); + assert(KeyLen > 1 && FlexBaseKey.substr(KeyLen - 1, 1) == "."); + return FlexBaseKey.substr(0, KeyLen - 1); + } + + void FinalizePreAssignCheck(Sema &SemaRef, ParentMap &PM, Expr *LastBoundsCheckExpr); + +public: + explicit DepGroup(DepGroupKind Kind, StringRef Key, StringRef FlexBaseKey) + : Kind(Kind), Key(Key), FlexBaseKey(FlexBaseKey) { + // Produce KeyDecls + } + + virtual ~DepGroup() {} + + void Finalize(Sema &S, ParentMap &PM); + + virtual void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) = 0; + + DepGroupKind getKind() const { return Kind; } + + Expr *getAssignExpr(size_t Index) const { + if (Index >= AssignExprList.size()) + return nullptr; + return AssignExprList[Index].getExpr(); + } + + void setSideEffectAfter() { SideEffectAfter = true; } + void clearSideEffectAfter() { SideEffectAfter = false; } + bool hasSideEffectAfter() const { return SideEffectAfter; } + void setSkipFlexCheck() { SkipFlexCheck = true; } + bool skipFlexCheck() const { return SkipFlexCheck; } + void setFlexBaseNull() { FlexBaseNull = true; } + bool isFlexBaseNull() const { return FlexBaseNull; } + + bool insertAssignedDecl(const ValueDecl *D, bool IsSelfAssign = false) { + auto It = AssignedDecls.find(D); + if (It != AssignedDecls.end()) + return false; + AssignedDecls[D] = AssignExprList.size(); + if (IsSelfAssign) + SelfAssignedDecls.insert(D); + return true; + } + bool insertDeclToNewValue(const ValueDecl *D, Expr *NewValue, unsigned Level) { + auto It = DeclToNewValue.find(D); + if (It != DeclToNewValue.end()) + return false; + DeclToNewValue[D] = std::make_pair(NewValue, Level); + return true; + } + + // This is in reverse order. + void insertMateredExpr(OpaqueValueExpr *OVE) { + MateredExprsUpFront.push_back(OVE); + } + + void insertMateredExprsReverse(llvm::SmallVectorImpl &OVEs) { + std::reverse(OVEs.begin(), OVEs.end()); + MateredExprsUpFront.append(OVEs); + } + + bool hasSelfAssign() const { return !SelfAssignedDecls.empty(); } + void addReferencedDecl(const ValueDecl *VD, unsigned Level) { + assert(!AssignExprList.empty()); + assert(!AssignExprList.back().IsSelfAssign()); + AssignExprList.back().AddReferencedDecl(VD, Level); + } + bool isDeclInitBasedGroup() const { return AssignExprList.empty(); } + void updateLastAssign(Expr *E, ValueDecl *VD, bool SelfAssign, bool CountVar, + bool CountForFam, bool FlexBase) { + AssignExprList.emplace_back(E, VD, SelfAssign, CountVar, CountForFam, + FlexBase); + } + Expr *getLastAssign() { + return AssignExprList.empty() ? nullptr + : AssignExprList[0].getAssignOrInitExpr(); + } + Expr *getFirstAssign() { + return AssignExprList.empty() + ? nullptr + : AssignExprList.rbegin()->getAssignOrInitExpr(); + } + + const AssignExprInfo &getFirstAssignExprInfoUnsafe() const { + return *AssignExprList.rbegin(); + } + + virtual bool DiagnoseMissingDependentAssign(Sema &SemaRef, const ValueDecl *VD) const = 0; + bool matchKey(StringRef K, const ValueDecl *VD) const { + return Key == K && KeyDecls.find(VD) != KeyDecls.end(); + } + bool matchFlexBaseKey(StringRef K) const { + return !FlexBaseKey.empty() && FlexBaseKey == K; + } + const Expr *getAssignExprForDecl(const ValueDecl *D) const { + auto It = AssignedDecls.find(D); + if (It == AssignedDecls.end() || It->second >= AssignExprList.size()) + return nullptr; + return AssignExprList[It->second].getExpr(); + } + + bool CheckDynamicCountAssignments(Sema &SemaRef); +}; + +void DepGroup::FinalizePreAssignCheck(Sema &SemaRef, + ParentMap &PM, + Expr *LastBoundsCheckExpr) { + if (!MateredExprsUpFront.empty()) { + std::reverse(MateredExprsUpFront.begin(), MateredExprsUpFront.end()); + bool FirstIsLast = (getFirstAssign() == getLastAssign()); + assert(getLastAssign()); + // FIXME: Binding comes first in the other places + Expr *UnbindExpr = FirstIsLast ? LastBoundsCheckExpr : getLastAssign(); + UnbindExpr = MaterializeSequenceExpr::Create(SemaRef.Context, + UnbindExpr, + MateredExprsUpFront, + /*Unbind*/true); + Expr *BindExpr = FirstIsLast ? UnbindExpr : LastBoundsCheckExpr; + BindExpr = MaterializeSequenceExpr::Create(SemaRef.Context, + BindExpr, + MateredExprsUpFront); + LastBoundsCheckExpr = BindExpr; + if (!FirstIsLast) { + bool Succ = ReplaceSubExpr(PM, getLastAssign(), UnbindExpr); + (void)Succ; + assert(Succ); + } + } + + if (getFirstAssign() != LastBoundsCheckExpr) { + bool Succ = ReplaceSubExpr(PM, getFirstAssign(), LastBoundsCheckExpr); + (void)Succ; + assert(Succ); + } +} + +static bool IsModifiableValue(Sema &S, const ValueDecl *VD) { + if (clang::IsConstOrLateConst(VD)) + return false; + Expr *TempLValueForVD = S.BuildDeclRefExpr( + const_cast(VD), VD->getType(), VK_LValue, SourceLocation()); + return TempLValueForVD->isModifiableLvalue(S.Context) == Expr::MLV_Valid; +} + +/// Finalize closes the current decl group analysis window. Before closing, +/// the function diagnoses the analyzed group to check if there was any missing +/// assignment to declaration within the same group. +void DepGroup::Finalize(Sema &SemaRef, ParentMap &PM) { + bool HadError = false; + for (const auto *Var : KeyDecls) { + /// We don't worry about instantiation here because we've already checked + /// the base as part of the group key. + if (AssignedDecls.find(Var) == AssignedDecls.end() && + IsModifiableValue(SemaRef, Var)) { + /// FIXME: Improve diagnostics using the base of field + SourceLocation Loc; + SourceRange Range; + /// We can have empty AssignExprList since we also track DeclStmt. + if (!AssignExprList.empty()) { + /// Continue if there was no directly dependent assignment to report. + /// In the following example, + /// we don't report on missing assignment to 'ptr2' and only report on + /// 'len' on which 'ptr' immediately depends. + /// \code + /// struct Foo { + /// int len; + /// int *__counted_by(len) ptr; + /// int *__counted_by(len) ptr2; + /// }; + /// struct Foo f; + /// f.ptr = nullptr; + /// \endcode + if (!DiagnoseMissingDependentAssign(SemaRef, Var)) + continue; + } else { + Loc = Var->getLocation(); + Range = Var->getSourceRange(); + SemaRef.Diag(Loc, diag::err_bounds_safety_non_adjacent_dependent_var_decl) + << getPrefixedName(Var) << Range; + } + HadError = true; + } + } + + /// This diagnoses if the consecutive assignments are ordered in a way to introduce + /// an unexpected runtime trap, as in the following example. + /// \code + /// struct S{ + /// unsigned len; + /// int *__counted_by(len) ptr1; + /// int *__counted_by(len) ptr2; + /// }; + /// void foo(struct S *s) { + /// s->ptr1 = s->ptr1 + 1; // ok: 's->ptr1' is referenced before updated. + /// s->ptr2 = s->ptr1; // error: cannot reference 's->ptr1' after it is changed during ... + /// s->len = 10; + /// } + /// \endcode + /// The following routine checks if a decl referenced in the assignment has an implicitly + /// dependent decl (e.g., 'ptr' is dependent to 'len' in the above example). If so, it + /// diagnoses if an assignment to the dependent decl (e.g., 'len') preceeds the current + /// assignment in the analyzed group. The same applies to '__ended_by'. + bool HasHadCountForFamError = false; + for (unsigned i = AssignExprList.size(); i--;) { + auto &ExprInfo = AssignExprList[i]; + if (ExprInfo.IsSelfAssign()) + continue; + + /// We skip bounds checks for single to single with flexible array member as + /// any other single to single cast. However, we still prevent the count + /// being silently unchecked by requiring the base to be initialized with a + /// wide pointer. + /// \code + /// flex = new_single_flex; + /// flex->count = new_count; + /// \endcode + if (ExprInfo.IsCountForFam() && + (!hasAssignToFlexBase() || (skipFlexCheck() && !isFlexBaseNull())) && + isFlexBasePointer() && !HasHadCountForFamError) { + const Expr *AssignedExpr = nullptr; + if (auto AssignOp = dyn_cast(ExprInfo.getExpr())) { + assert(AssignOp->getOpcode() == BO_Assign); + AssignedExpr = AssignOp->getLHS(); + } else if (auto UOp = dyn_cast(ExprInfo.getExpr())) { + AssignedExpr = UOp->getSubExpr(); + } + assert(AssignedExpr); + std::string AssignedExprString; + llvm::raw_string_ostream AssignedExprStringOS(AssignedExprString); + computeMemberExprKey(AssignedExpr, AssignedExprStringOS, /*Top*/ false); + const bool IsFirstNonFlexBase = + i == AssignExprList.size() - 1 || + (hasAssignToFlexBase() && i == AssignExprList.size() - 2); + SemaRef.Diag(ExprInfo.getExpr()->getExprLoc(), + diag::err_bounds_safety_no_preceding_fam_base_assignment) + << AssignedExprString << getFlexBaseName() << IsFirstNonFlexBase; + if (skipFlexCheck() && hasAssignToFlexBase()) { + SemaRef.Diag(getFirstAssign()->getExprLoc(), + diag::note_bounds_safety_flex_base_assign) + << getFlexBaseName(); + } else if (i < AssignExprList.size() - 1) { + SemaRef.Diag(getFirstAssign()->getExprLoc(), + diag::note_bounds_safety_first_dep_assign) + << getFlexBaseName(); + } + HadError = HasHadCountForFamError = true; + } + + for (auto It : ExprInfo.getReferencedDecls()) { + const auto *RefDecl = cast(It.first); + + auto RefUpdateIt = AssignedDecls.find(RefDecl); + if (RefUpdateIt != AssignedDecls.end() && RefUpdateIt->second > i) { + auto *RefUpdateExpr = getAssignExprForDecl(RefDecl); + // Referenced after updated. + SemaRef.Diag(ExprInfo.getExpr()->getExprLoc(), + diag::err_bounds_safety_dependent_assignments_order) + << getPrefixedName(RefDecl); + SemaRef.Diag(RefUpdateExpr->getExprLoc(), diag::note_bounds_safety_decl_assignment) + << getPrefixedName(RefDecl); + HadError = true; + } + } + } + + HadError |= CheckDynamicCountAssignments(SemaRef); + + if (HadError) + return; + + EmitChecksToAST(SemaRef, PM); +} + +// Check assignment to dynamic count pointer. Use the computed dependent values +// within the assignemnt group and pass them to +// CheckDynamicCountSizeForAssignment() to check all constraints. +bool DepGroup::CheckDynamicCountAssignments(Sema &SemaRef) { + bool HadError = false; + + for (const auto &ExprInfo : AssignExprList) { + const auto *BinOp = + dyn_cast(ExprInfo.getAssignOrInitExpr()); + if (!BinOp || BinOp->getOpcode() != BO_Assign) + continue; + + const Expr *LHS = BinOp->getLHS(); + QualType LHSTy = LHS->getType(); + const auto *DCPT = LHSTy->getAs(); + if (!DCPT) + continue; + + Expr *RHS = BinOp->getRHS(); + + // Ignore Parens, ImpCasts and OVEs in RHS. + auto IgnoreOVE = [](Expr *E) -> Expr * { + if (auto *OVE = dyn_cast(E)) + return OVE->getSourceExpr(); + return E; + }; + RHS = IgnoreExprNodes(RHS, IgnoreParensSingleStep, + IgnoreImplicitCastsSingleStep, IgnoreOVE); + + const ValueDecl *VD = nullptr; + if (const auto *DRE = dyn_cast(LHS)) + VD = DRE->getDecl(); + + Expr *LHSMemberBase = nullptr; + if (auto *ME = dyn_cast(LHS)) + LHSMemberBase = ME->getBase(); + + if (!SemaRef.CheckDynamicCountSizeForAssignment( + LHSTy, RHS, Sema::AA_Assigning, BinOp->getExprLoc(), + VD ? VD->getName() : StringRef(), DeclToNewValue, LHSMemberBase)) { + HadError = true; + } + } + + return HadError; +} + +class CountDepGroup : public DepGroup { +protected: + static ExprResult MakeWidePtrFromDecl(Sema &SemaRef, ValueDecl *VD, + SourceLocation Loc) { + auto *DRE = SemaRef.BuildDeclRefExpr(VD, VD->getType(), VK_LValue, Loc); + return SemaRef.DefaultLvalueConversion(DRE); + } + + void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) override { + auto *const FA = getFirstAssign(); + if (!FA) + return; + if (const auto *AE = dyn_cast(FA)) { + assert(BinaryOperator::isAssignmentOp(AE->getOpcode())); + if (SemaRef.allowBoundsUnsafePointerAssignment( + AE->getLHS()->getType(), AE->getRHS(), AE->getExprLoc())) + return; + } + PreAssignCheck Builder(SemaRef, PM, FA, DeclToNewValue); + /// We insert information on delayed or non-delayed checks for dynamic count + /// assignments. + /// BoundsSafety mandates the assignments to decl within a group should be all + /// or nothing which means the user needs to add a self assignment for + /// unchanged variables. If there was a self assignment to a dynamic count + /// pointer, the result of count expression with the new count can't be + /// bigger than the result with the old count since the bound of the pointer + /// hasn't changed. + if (hasSelfAssign() || hasFlexBase()) { + for (const auto &ExprInfo : AssignExprList) { + if (!ExprInfo.IsCountVar() || ExprInfo.IsSelfAssign()) + continue; + const Expr *E = ExprInfo.getExpr()->IgnoreParenImpCasts(); + const auto *UO = dyn_cast(E); + assert(!UO || UO->isIncrementDecrementOp()); + if (!UO || !UO->isIncrementOp()) + continue; + + const ValueDecl *VD = ExprInfo.getValueDecl(); + auto *Attr = VD->getAttr(); + for (unsigned i = 0; i < Attr->dependerDecls_size(); ++i) { + unsigned DepLevel = Attr->dependerLevels_begin()[i]; + auto *DepVD = cast(Attr->dependerDecls_begin()[i]); + auto It = AssignedDecls.find(DepVD); + bool NoDepAssign = It == AssignedDecls.end(); + assert(!NoDepAssign || ExprInfo.IsCountForFam()); + if (NoDepAssign) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_increment_dynamic_count_nodep) + << getPrefixedName(VD, Attr->getIsDeref()); + } else if (AssignExprList[It->second].IsSelfAssign()) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_increment_dynamic_count) + << getPrefixedName(VD, Attr->getIsDeref()) + << getPrefixedName(DepVD, DepLevel); + return; + } + } + } + } + + // For flexible base as, having at least two expressions in the group + // means it has a member assignment. + auto *MemberAccess = getAssignExpr(AssignExprList.size() - 2); + if (MemberAccess && isFlexBaseNull()) { + assert(skipFlexCheck()); + // Report error as there is a subsequent member access on this NULL + // base. + SemaRef.Diag(MemberAccess->getExprLoc(), + diag::err_bounds_safety_member_reference_null_base); + return; + } + + if (SemaRef.getDiagnostics().hasUnrecoverableErrorOccurred()) + return; + + const auto &FirstExprInfo = getFirstAssignExprInfoUnsafe(); + if (FirstExprInfo.IsFlexBase()) { + if (!skipFlexCheck()) { + Builder.buildAndChainFlex( + DeclToNewValue[FirstExprInfo.getValueDecl()].first); + } + } else if (FirstExprInfo.IsCountForFam()) { + Builder.buildAndChainOldCountCheck(FirstExprInfo.getExpr()); + } + for (const auto &ExprInfo : AssignExprList) { + if (ExprInfo.IsFlexBase() || ExprInfo.IsCountForFam()) + continue; + const auto *Expr = ExprInfo.getExpr(); + const auto *VD = ExprInfo.getValueDecl(); + Builder.buildAndChain(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + if (isValueDeclOutCount(VD)) { + const auto *Att = VD->getAttr(); + for (Decl *DepD : Att->dependerDecls()) { + auto *DepVD = dyn_cast(DepD); + if (!DepVD || isValueDeclOutBuf(DepVD)) + continue; + if (DeclToNewValue.find(DepVD) != DeclToNewValue.end()) + continue; + auto WidePtrR = + MakeWidePtrFromDecl(SemaRef, DepVD, Expr->getBeginLoc()); + if (!WidePtrR.get()) + return; + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, WidePtrR.get()); + insertMateredExpr(OVE); + Builder.buildAndChain(DepVD->getType(), OVE); + } + } + } + FinalizePreAssignCheck(SemaRef, PM, Builder.getChainedBoundsCheckExpr()); + } + + bool DiagnoseMissingDependentAssign(Sema &SemaRef, const ValueDecl *VD) const override { + if (!VD->getType()->isIntegralOrEnumerationType() && + !VD->getType()->isPointerType()) + return false; + if (auto *Att = VD->getAttr()) { + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + const ValueDecl *AssignedDecl = nullptr; + unsigned PtrLevel = 0; + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const Decl *D = Att->dependerDecls_begin()[i]; + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + + if (KeyVD->getType()->isIncompleteArrayType() && + hasAssignToFlexBase()) { + // This must be flexible array member and `VD` must be referred + // to by the count expression for fam. + const auto *DCPTy = KeyVD->getType()->getAs(); + assert(DCPTy); + bool HasFlexDepAssign = false; + unsigned FirstFlexAssignIndex = 0; + const ValueDecl *FlexAssignedDecl = nullptr; + unsigned FlexPtrLevel = 0; + for (const auto &DI : DCPTy->getCoupledDecls()) { + const auto *D = DI.getDecl(); + if (D == VD) + continue; + assert(isa(D)); + const auto *DepVD = cast(D); + auto It = AssignedDecls.find(DepVD); + // Report the missing assignment error only if there is an + // assignment to any other decl referred to by the same count + // expression of fam. + // Find the first assignment (the bigger the index, the earlier the statement is) + // that creates a direct dependency with 'VD'. + if (It != AssignedDecls.end() && + // Don't report missing assignments for structs containing a + // referenced field. + DepVD->getType()->isIntegralOrEnumerationType() && + (!HasFlexDepAssign || FirstFlexAssignIndex < It->second)) { + HasFlexDepAssign = true; + FirstFlexAssignIndex = It->second; + FlexAssignedDecl = DepVD; + FlexPtrLevel = DI.isDeref(); + } + } + if (HasFlexDepAssign) { + const auto &ExprInfo = AssignExprList[FirstFlexAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + QualType DependerType = getInnerType(FlexAssignedDecl->getType(), FlexPtrLevel); + DeclRefFinder VDFinder(SemaRef.getASTContext(), + DCPTy->getCountExpr(), VD); + const Expr *VDExpr = VDFinder.getExpr(); + assert(VDExpr); + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(FlexAssignedDecl, FlexPtrLevel) + << getPrefixedNameWithFlexBase(VDExpr, Att->getIsDeref()) + << DependerType << 0; + return true; + } + } else { + auto It = AssignedDecls.find(KeyVD); + // Find the first assignment (the bigger the index, the earlier the statement is) + // that creates a direct dependency with 'VD'. + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + PtrLevel = Att->dependerLevels_begin()[i]; + } + } + } + if (HasDepAssign) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + QualType DependerType = getInnerType(AssignedDecl->getType(), PtrLevel); + + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, PtrLevel) + << getPrefixedName(VD, Att->getIsDeref()) << DependerType << 0; + } + return HasDepAssign; + } + const auto *DCPTy = VD->getType()->getAs(); + unsigned PtrLevel = 0; + if (!DCPTy && VD->getType()->isPointerType()) { + DCPTy = VD->getType()->getPointeeType()->getAs(); + PtrLevel = 1; + } + if (!DCPTy) + return false; + assert(DCPTy); + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + const ValueDecl *AssignedDecl = nullptr; + unsigned AssigneeLevel = 0; + // We don't force the user to assign VD if VD is non-inout buf parameter and + // all dependent declarations are inout counts. + bool CanSkipAssign = isa(VD) && !isValueDeclOutBuf(VD); + for (const auto &DI : DCPTy->dependent_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + auto It = AssignedDecls.find(KeyVD); + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + AssigneeLevel = DI.isDeref(); + } + CanSkipAssign &= isValueDeclOutCount(D); + } + bool Diagnose = HasDepAssign && !CanSkipAssign; + if (Diagnose) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, AssigneeLevel) + << getPrefixedName(VD, PtrLevel) << QualType(DCPTy, 0) << 1; + } + return Diagnose; + } + +public: + explicit CountDepGroup(const ValueDecl *VD, StringRef Key, + StringRef FlexBaseKey, const RecordDecl *FlexBaseDecl) + : DepGroup(DGK_Count, Key, FlexBaseKey) { + std::function AddDependentDecls; + AddDependentDecls = [this, &AddDependentDecls, + &FlexBaseDecl](const ValueDecl *VD) { + if (FlexBaseDecl && !FlexBaseDecl->isParentStructOf(VD)) + return; // if this is a nested field referred to by outer structs, + // only consider fields in the scope of the relevant struct. + if (!KeyDecls.insert(VD).second) + return; + + // Skip adding decls that use a const/late const. E.g.: + // + // ``` + // extern const unsigned extern_const_global_count; + // + // void fun_extern_const_global_count( + // int *__counted_by(extern_const_global_count) arg); + // + // void test_local_extern_const_global_count() { + // int *__counted_by(extern_const_global_count) local; + // } + // ``` + // + // For the case above the loop below is going over the dependent decls + // of `extern_const_global_count` when we are running DCPAA on + // `test_local_extern_const_global_count`. We don't want to add + // all other uses of the count outside of function (i.e. `arg` in this + // example) otherwise we will emit an incorrect diagnostic (`local + // variable arg must be declared right next to its dependent decl`) which + // doesn't make sense because `arg` isn't in the function being analyzed. + if (!clang::IsConstOrLateConst(VD)) { + if (auto *Att = VD->getAttr()) { + for (const auto *D : Att->dependerDecls()) { + // XXX: We could've just make it ValueDecl. + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + } + } + + const auto *DCPTy = VD->getType()->getAs(); + if (!DCPTy && VD->getType()->isPointerType()) { + DCPTy = VD->getType()->getPointeeType()->getAs(); + } + + if (!DCPTy) + return; + + for (const auto &DI : DCPTy->dependent_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + }; + + AddDependentDecls(VD); + } + + static CountDepGroup *Create(const ValueDecl *VD, StringRef Key, + StringRef FlexBaseKey, + const RecordDecl *FlexBaseDecl) { + return new CountDepGroup(VD, Key, FlexBaseKey, FlexBaseDecl); + } + + /// LLVM RTTI Interface + static bool classof(const DepGroup *DG) { + return DG->getKind() == DGK_Count; + } +}; + +class RangeDepGroup : public DepGroup { +protected: + void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) override { + if (!getFirstAssign() || SemaRef.hasUncompilableErrorOccurred()) + return; + else if (const auto *AE = dyn_cast(getFirstAssign())) { + assert(BinaryOperator::isAssignmentOp(AE->getOpcode())); + if (SemaRef.allowBoundsUnsafePointerAssignment( + AE->getLHS()->getType(), AE->getRHS(), AE->getExprLoc())) + return; + } + PreAssignCheck Builder(SemaRef, PM, getFirstAssign(), DeclToNewValue); + bool HadOldRangeCheck = false; + /// If there is only one actual update, we just check the range of that actual assignment. + if (hasSelfAssign() && AssignedDecls.size() == SelfAssignedDecls.size() + 1) { + for (const auto &ExprInfo : AssignExprList) { + // XXX: We should just prevent having multiple assignments to the same decl in the + // same analysis region (or basic block). + if (!ExprInfo.IsSelfAssign()) { + if (SemaRef.allowBoundsUnsafePointerAssignment( + ExprInfo.getValueDecl()->getType(), + DeclToNewValue[ExprInfo.getValueDecl()].first, + ExprInfo.getExpr()->getExprLoc())) + return; + HadOldRangeCheck = Builder.buildAndChainOldRangeCheck(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + break; + } + } + } + if (!HadOldRangeCheck) { + for (auto &ExprInfo : llvm::reverse(AssignExprList)) { + if (SemaRef.allowBoundsUnsafePointerAssignment( + ExprInfo.getValueDecl()->getType(), + DeclToNewValue[ExprInfo.getValueDecl()].first, + ExprInfo.getExpr()->getExprLoc())) + return; + Builder.buildAndChain(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + } + } + FinalizePreAssignCheck(SemaRef, PM, Builder.getChainedBoundsCheckExpr()); + } + + bool DiagnoseMissingDependentAssign(Sema &SemaRef, + const ValueDecl *VD) const override { + unsigned PtrLevel = 0; + const auto *DRPTy = VD->getType()->getAs(); + if (!DRPTy && VD->getType()->isPointerType()) { + DRPTy = VD->getType()->getPointeeType()->getAs(); + PtrLevel = 1; + } + if (!DRPTy) + return false; + assert(DRPTy); + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + QualType DependerType; + const ValueDecl *AssignedDecl = nullptr; + unsigned AssigneeLevel = 0; + unsigned VDLevel = 0; + auto FindPrecedingAssignIndex = [&](const TypeCoupledDeclRefInfo &DI, + const DynamicRangePointerType *DRPTy) { + if (PtrLevel != (unsigned)DI.isDeref()) + return; + const auto *D = DI.getDecl(); + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + auto It = AssignedDecls.find(KeyVD); + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + AssigneeLevel = DI.isDeref(); + DependerType = DRPTy ? QualType(DRPTy, 0) : cast(D)->getType(); + DependerType = getInnerType(DependerType, PtrLevel); + } + }; + for (const auto &DI : DRPTy->getEndPtrDecls()) { + FindPrecedingAssignIndex(DI, DRPTy); + } + for (const auto &DI : DRPTy->getStartPtrDecls()) { + FindPrecedingAssignIndex(DI, nullptr); + } + if (HasDepAssign) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, AssigneeLevel) + << getPrefixedName(VD, VDLevel) << DependerType << 0; + } + return HasDepAssign; + } + +public: + explicit RangeDepGroup(const ValueDecl *VD, StringRef Key) + : DepGroup(DGK_Range, Key, StringRef()) { + std::function AddDependentDecls; + AddDependentDecls = [this, &AddDependentDecls](const ValueDecl *VD) { + if (!KeyDecls.insert(VD).second) + return; + + const auto *DRPTy = VD->getType()->getAs(); + if (!DRPTy && VD->getType()->isPointerType()) { + DRPTy = VD->getType()->getPointeeType()->getAs(); + } + + if (!DRPTy) + return; + + for (const auto &DI : DRPTy->endptr_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + + for (const auto &DI : DRPTy->startptr_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + }; + + AddDependentDecls(VD); + } + + static RangeDepGroup *Create(const ValueDecl *VD, StringRef Key) { + return new RangeDepGroup(VD, Key); + } + + /// LLVM RTTI Interface + static bool classof(const DepGroup *DG) { + return DG->getKind() == DGK_Range; + } +}; + +/// CheckCountAttributedDeclAssignments - This tracks a group of assignments to +/// decl with dynamic count dependency, assuming each Stmt is analyzed backward +/// through "BeginStmt(Stmt)". This reports an error if there is a side effect +/// between assignments within the same group. For example, +/// @code +/// int len; +/// int *__counted_by(len + 1) ptr; +/// ... +/// len = ...; +/// function_with_side_effect(); +/// ptr = ...; +/// @endcode +/// "len" and "ptr" are in the same DepGroup and the analyzer detects the side +/// effect in between assignments to these variables in the same group and +/// reports an error. +class CheckCountAttributedDeclAssignments + : public RecursiveASTVisitor { + + using BaseVisitor = RecursiveASTVisitor; + + Sema &SemaRef; + AnalysisDeclContext &AC; + std::unique_ptr CurrentDepGroup; + bool InAssignToDepRHS = false; + unsigned AssigneeLevel = 0; + QualType AssigneeType; + bool EarlyLookup = false; + llvm::SmallPtrSet DelayedStmts; + llvm::SmallPtrSet VisitedILEInCompoundLiteralExprs; + llvm::SmallPtrSet DiagnosedPtrIncDec; + + void Initialize() { + InAssignToDepRHS = false; + AssigneeLevel = 0; + AssigneeType = QualType(); + } + + void PushDelayedStmt(Stmt *S) { DelayedStmts.insert(S); } + + bool CheckAndPopDelayedStmt(Stmt *S) { return DelayedStmts.erase(S); } + + bool MatchDepGroup(const AssignedDeclRefResult &Result) { + if (!CurrentDepGroup) + return false; + + if (Result.IsFlexBase) { + // XXX: I could just calculate the exact base from the beginning? + std::string FlexBaseKey = + Result.Key + Result.ThisVD->getName().str() + "->"; + return CurrentDepGroup->matchFlexBaseKey(FlexBaseKey); + } + if (!Result.FlexBaseKey.empty()) + return CurrentDepGroup->matchFlexBaseKey(Result.FlexBaseKey); + + return CurrentDepGroup->matchKey(Result.Key, Result.ThisVD); + } + + /// Assignee is either DeclRef or MemberExpr that is LHS of assignment. + bool InsertAssignedDecl(const AssignedDeclRefResult &Result, + bool IsSelfAssign, + bool IsVarDecl = false); + void RegisterDeclAssignment(Expr *AssignExpr, AssignedDeclRefResult &Result, + bool IsSelfAssign); + + void UpdateLastAssign(Expr *E, const AssignedDeclRefResult &Result, + bool IsSelfAssign) { + if (EarlyLookup) + return; + assert(CurrentDepGroup && "Empty dependent length"); + CurrentDepGroup->updateLastAssign(E, Result.ThisVD, IsSelfAssign, + Result.IsCountVar, Result.IsCountForFam, + Result.IsFlexBase); + } + + void SetSideEffectAfter() { + if (EarlyLookup) + return; + if (CurrentDepGroup) + CurrentDepGroup->setSideEffectAfter(); + } + + void FinalizeDepGroup() { + if (CurrentDepGroup) { + CurrentDepGroup->Finalize(SemaRef, getParentMap()); + CurrentDepGroup.reset(); + } + } + + bool HasDepGroup() const { return CurrentDepGroup != nullptr; } + + bool InCountDepGroup() const { return CurrentDepGroup && isa(CurrentDepGroup); } + bool InRangeDepGroup() const { return CurrentDepGroup && isa(CurrentDepGroup); } + void HandleRHSOfAssignment(BinaryOperator *E); + void HandleVarInit(VarDecl *VD); + void ProcessDeclRefInRHSRecursive(Expr *E); + + ParentMap &getParentMap() { return AC.getParentMap(); } + +public: + explicit CheckCountAttributedDeclAssignments(Sema &SemaRef, + AnalysisDeclContext &AC) + : SemaRef(SemaRef), AC(AC) {} + + ~CheckCountAttributedDeclAssignments() { + /// Finalize last assign expressions. + FinalizeDepGroup(); + } + + bool VisitCallExpr(CallExpr *E); + bool TraverseBinaryOperator(BinaryOperator *E); + bool TraverseCompoundAssignOperator(CompoundAssignOperator *E) { + return TraverseBinaryOperator(E); + } + bool TraverseUnaryOperator(UnaryOperator *E); + bool TraverseStmtExpr(StmtExpr *E) { return true; } + bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) { + return TraverseStmt(E->getSourceExpr()); + } + bool TraverseBoundsCheckExpr(BoundsCheckExpr *E) { + return TraverseStmt(E->getGuardedExpr()); + } + bool TraverseMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + return TraverseStmt(E->getWrappedExpr()); + } + bool TraverseBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + return TraverseStmt(E->getSubExpr()); + } + bool VisitVarDecl(VarDecl *VD); + bool TraverseDeclStmt(DeclStmt *S); + bool VisitCompoundLiteralExpr(CompoundLiteralExpr *CLE); + bool TraverseReturnStmt(ReturnStmt *S); + + Expr *HandleInitListExpr(InitListExpr *IL, PreAssignCheck &Builder); + void BeginStmt(Stmt *S); +}; +} // namespace + +/// This function generates the flexible struct base. +/// This function finds the flexible struct pointer base that can be updated +/// with a bounded pointer. For `a.c.f->s.b.count = xxx`, as an example, +/// `a.c.f->` is identified as the flexible pointer base. If there is no pointer +/// base, this returns the entire struct base. +/// The access path is emitted to @p OS if provided. +/// The RecordDecl of the struct containing the flexible array member is +/// returned if found. +RecordDecl *DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey( + Expr *InE, llvm::raw_string_ostream *OS) { + Expr *E = InE->IgnoreParenCasts(); + RecordDecl *BaseRD = nullptr; + while (auto ME = dyn_cast(E)) { + // Stripping implicit casts of the base allows us to distinguish between + // __bidi_indexable vs. __single implicitly promoted to __bidi_indexable. + QualType BaseTy = ME->getBase()->IgnoreImpCasts()->getType(); + // The closest pointer base becomes the flexible base. + if (ME->isArrow()) { + // The base is a pointer, but since we ignore implicit casts we + // skip over the decay from array to pointer type. + assert(BaseTy->isPointerType() || BaseTy->isArrayType()); + if (!BaseTy->isSinglePointerType() || BaseTy->isBoundsAttributedType()) + return nullptr; + if (auto *RD = BaseTy->getPointeeType()->getAsRecordDecl()) { + if (!RD->hasFlexibleArrayMember()) + return nullptr; + if (OS) + computeMemberExprKey(ME, *OS); + return RD; + } + return nullptr; + } + BaseRD = BaseTy->getAsRecordDecl(); + E = ME->getBase()->IgnoreParenCasts(); + } + + // There is no pointer base. Emit the whole lvalue base. + auto *EndME = dyn_cast(InE->IgnoreParenCasts()); + if (!EndME || !BaseRD->hasFlexibleArrayMember()) + return nullptr; + if (OS) + computeMemberExprKey(EndME, *OS); + return BaseRD; +} + +static DepGroup *CreateDepGroup(const AssignedDeclRefResult &Result) { + if (Result.IsRangePtrVar) + return RangeDepGroup::Create(Result.ThisVD, Result.Key); + else + return CountDepGroup::Create(Result.ThisVD, Result.Key, Result.FlexBaseKey, + Result.FlexBaseDecl); +} + +bool CheckCountAttributedDeclAssignments::InsertAssignedDecl( + const AssignedDeclRefResult &Result, bool IsSelfAssign, bool IsVarDecl) { + if (EarlyLookup) + return false; + + /// If the current group tracks assignments, finalize it. + if (!MatchDepGroup(Result) || + (IsVarDecl && CurrentDepGroup && CurrentDepGroup->getLastAssign())) { + FinalizeDepGroup(); + CurrentDepGroup.reset(CreateDepGroup(Result)); + } + + AssigneeLevel = Result.Level; + AssigneeType = Result.Ty; + if (CurrentDepGroup->hasSideEffectAfter()) { + SourceLocation Loc = CurrentDepGroup->getLastAssign() + ? CurrentDepGroup->getLastAssign()->getExprLoc() + : Result.Loc; + SourceRange Range = + CurrentDepGroup->getLastAssign() + ? SourceRange(Result.Loc, + CurrentDepGroup->getLastAssign()->getEndLoc()) + : SourceRange(); + + SemaRef.Diag(Loc, diag::err_bounds_safety_dependent_vars_assign_side_effect) + << Range; + /// Keep tracking the dep group as the error has been reported. + CurrentDepGroup->clearSideEffectAfter(); + } + const ValueDecl *VD = Result.ThisVD; + assert(VD); + return CurrentDepGroup->insertAssignedDecl(VD, IsSelfAssign); +} + +void CheckCountAttributedDeclAssignments::BeginStmt(Stmt *S) { + Initialize(); + /// Currently, we only detect a group of adjacent simple assignments. + switch (S->getStmtClass()) { + case Stmt::DeclRefExprClass: + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + case Stmt::CallExprClass: + case Stmt::UnaryExprOrTypeTraitExprClass: + case Stmt::UnaryOperatorClass: + case Stmt::DeclStmtClass: + break; + default: + FinalizeDepGroup(); + } + TraverseStmt(S); +} + +namespace { + +Expr *findSourceExpr(Expr *E) { + if (!E) + return nullptr; + E = E->IgnoreParens(); + if (auto CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_BoundsSafetyPointerCast) + return CE->getSubExpr(); + } + return E; +} + +// Get the mapping from dependent fields and non-param variables to their +// initializers. If a field/variable doesn't have an initializer, create an +// implicit one (zero-init). +Sema::DependentValuesMap getDependentInits(Sema &SemaRef, + const CountAttributedType *DCPT, + InitListExpr *ILE) { + Sema::DependentValuesMap DependentValues; + for (auto &DI : DCPT->dependent_decls()) { + // Fields and non-param variables cannot have dependent inout counts. + assert(!DI.isDeref()); + + ValueDecl *D = DI.getDecl(); + auto *VD = dyn_cast(D); + auto *FD = dyn_cast(D); + assert(FD || (VD && !isa(VD))); + + // We allow the dependent decl to be an __unsafe_late_const, but we don't + // know its value, thus we don't replace it. + if (VD && VD->hasAttr()) + continue; + + Expr *Init; + if (VD && VD->hasInit()) { + Init = VD->getInit(); + } else if (FD && ILE && FD->getFieldIndex() < ILE->getNumInits()) { + Init = ILE->getInit(FD->getFieldIndex()); + } else { + // Assume zero-init when there is no initializer. + Init = new (SemaRef.Context) ImplicitValueInitExpr(D->getType()); + } + + DependentValues[D] = {Init, /*Level=*/0}; + } + return DependentValues; +} + +bool diagnoseDynamicCountVarInit(Sema &SemaRef, SourceLocation Loc, + const Twine &Designator, QualType Ty, + Expr *Init, InitListExpr *ParentInit) { + const auto *DCPT = Ty->getAs(); + if (DCPT && Ty->isPointerType()) { + auto DependentValues = getDependentInits(SemaRef, DCPT, ParentInit); + + Expr *RHS; + if (Init) + RHS = Init->IgnoreParenImpCasts(); + else + RHS = new (SemaRef.Context) ImplicitValueInitExpr(Ty); + + return !SemaRef.CheckDynamicCountSizeForAssignment( + Ty, RHS, Sema::AA_Initializing, Loc, Designator, DependentValues); + } + + auto GetChildLoc = [&](Expr *ChildInit) { + // If we have an explicit init, get the location from it. + if (ChildInit && !isa(ChildInit)) + return ChildInit->getBeginLoc(); + + // For implicit inits, we point to the '}' in the parent initializer list, + // for example: + // struct foo { int len; int *__counted_by(len) p; }; + // struct foo f = { 1 }; + // ^ diagnostic for 'p' here + if (const auto *ILE = dyn_cast_or_null(Init)) + return ILE->getEndLoc(); + + // Otherwise, point at the parent location (e.g., the VarDecl's location). + return Loc; + }; + + auto GetInitListExpr = [&SemaRef](Expr *Init) -> InitListExpr * { + if (!Init) + return nullptr; + auto *ILE = dyn_cast(Init); + if (ILE) + return ILE; + + // Try CompoundLiteralExpr assignment. + // E.g. + // struct Foo a = (struct Foo) { .field = 0x0 }; + + // Checking CompoundLiteralExpr assignment is currently guarded by the + // new bounds check to avoid potential build failures. + // TODO: We should **always** check CompoundLiteralExprs (rdar://138982703). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_CompoundLiteralInit)) + return nullptr; + auto *CLE = + dyn_cast_if_present(Init->IgnoreImpCasts()); + if (!CLE) + return nullptr; + ILE = dyn_cast_if_present(CLE->getInitializer()); + return ILE; + }; + + if (const auto *RD = Ty->getAsRecordDecl()) { + assert(RD->isStruct() || RD->isUnion()); + + auto *ILE = GetInitListExpr(Init); + + // If RD is an union and we know which field is being initialized, check + // only that field and ignore the rest. + if (RD->isUnion() && ILE) { + const FieldDecl *FD = ILE->getInitializedFieldInUnion(); + Expr *ChildInit = ILE->getNumInits() > 0 ? ILE->getInit(0) : nullptr; + return diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), ChildInit, ILE); + } + + bool HadError = false; + for (const auto *FD : RD->fields()) { + Expr *ChildInit = nullptr; + if (ILE && FD->getFieldIndex() < ILE->getNumInits()) + ChildInit = ILE->getInit(FD->getFieldIndex()); + HadError |= diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), ChildInit, ILE); + } + return HadError; + } + + if (const auto *CAT = SemaRef.Context.getAsConstantArrayType(Ty)) { + assert(CAT->getSize().isNonNegative()); + uint64_t Size = CAT->getSize().getZExtValue(); + auto *ILE = GetInitListExpr(Init); + + // Don't interate over the array elements if the element type is + // uninteresting for the diagnosis. + QualType ET = CAT->getElementType(); + if (!ET->isCountAttributedType() && !ET->getAsRecordDecl() && + !SemaRef.Context.getAsConstantArrayType(ET)) { + return false; + } + + // Avoid complaining multiple times for implicit init, since the errors are + // the same. + uint64_t MaxSize = !ILE ? 1 : (ILE->getNumInits() + 1); + if (MaxSize < Size) + Size = MaxSize; + + bool HadError = false; + for (uint64_t I = 0; I < Size; ++I) { + Expr *ChildInit = nullptr; + if (ILE && I < ILE->getNumInits()) + ChildInit = ILE->getInit(I); + HadError |= diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('[') + .concat(llvm::utostr(I)) + .concat(llvm::Twine(']'))), + ET, ChildInit, nullptr); + } + return HadError; + } + + return false; +} + +} // namespace + +bool Sema::DiagnoseDynamicCountVarZeroInit(VarDecl *VD) { + if (VD->hasExternalStorage()) + return false; + + return diagnoseDynamicCountVarInit(*this, VD->getLocation(), VD->getName(), + VD->getType(), VD->getInit(), + /*ParentInit=*/nullptr); +} + +namespace { + +bool diagnoseRecordInitsImpl( + Sema &SemaRef, InitListExpr *IL, bool &NeedPreCheck, + bool DiagnoseAssignments, + SmallVectorImpl &InitializersWithSideEffects) { + bool HadError = false; + + if (SemaRef.Context.getAsArrayType(IL->getType())) { + for (unsigned i = 0; i < IL->getNumInits(); ++i) { + if (auto SubIL = dyn_cast(IL->getInit(i))) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + } + } + return HadError; + } + + auto RD = IL->getType()->getAsRecordDecl(); + if (!RD) + return true; + + if (RD->isUnion()) { + auto FD = IL->getInitializedFieldInUnion(); + if (FD && IL->getNumInits()) { + if (auto SubIL = dyn_cast(IL->getInit(0))) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + } + } + return HadError; + } + + // We memoize null-pointer evaluation results for each init so we don't + // repeat the same evaluation. + enum NullState { NS_Unknown, NS_Null, NS_Nonnull }; + llvm::SmallVector InitIsNull(IL->getNumInits(), NS_Unknown); + unsigned InitIndex = 0; + for (auto FD : RD->fields()) { + assert(InitIndex < IL->getNumInits()); + unsigned CurrInitIndex = InitIndex; + Expr *Init = IL->getInit(InitIndex++); + if (auto SubIL = dyn_cast(Init)) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + continue; + } + if (SemaRef.allowBoundsUnsafePointerAssignment(FD->getType(), Init, + Init->getExprLoc())) + continue; + + auto isNullPtrInit = [&](unsigned Idx, ASTContext &Context) -> bool { + assert(Idx < IL->getNumInits()); + if (InitIsNull[Idx] != NS_Unknown) + return InitIsNull[Idx] == NS_Null; + Expr *ThisInit = IL->getInit(Idx); + bool IsNull = + isa(ThisInit) || + ThisInit->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull) != Expr::NPCK_NotNull; + InitIsNull[Idx] = IsNull ? NS_Null : NS_Nonnull; + return IsNull; + }; + + // XXX: rdar://76568300 + if (Init->HasSideEffects(SemaRef.Context)) { + InitializersWithSideEffects.emplace_back(Init); + SourceLocation Loc = Init->getExprLoc(); + if (auto *Att = FD->getAttr()) { + NeedPreCheck = true; + if (!Att->getIsDeref()) { + assert(Att->dependerDecls_size() > 0); + const auto *Depender = *Att->dependerDecls_begin(); + assert(isa(Depender)); + const auto *DepVD = cast(Depender); + const auto *DepDCPTy = DepVD->getType()->getAs(); + assert(DepDCPTy); + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << (DepDCPTy->isCountInBytes() ? 1 : 0); + HadError = true; + continue; + } + } + if (const auto *DCPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << DCPTy->getKind() + 2; + HadError = true; + continue; + } + if (const auto *DRPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << (DRPTy->getEndPointer() ? 6 : 7); + HadError = true; + continue; + } + } + + if (auto *OrigDCPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + if (OrigDCPTy->isPointerType() && DiagnoseAssignments) { + // If requested try to diagnose assignments. This is currently + // only for CompoundLiteralExpr initializer lists. Variable initializers + // are handled elsewhere (DepGroup::CheckDynamicCountAssignments). + auto DependentValues = getDependentInits(SemaRef, OrigDCPTy, IL); + HadError |= !SemaRef.CheckDynamicCountSizeForAssignment( + FD->getType(), Init, Sema::AA_Initializing, + Init->getExprLoc(), StringRef(), DependentValues); + } + } else if (auto *OrigDRPTy = + FD->getType()->getAs()) { + NeedPreCheck = true; + bool IsImplicitInitExpr = isa(Init); + if (isNullPtrInit(CurrInitIndex, SemaRef.Context)) { + auto diagnosePartialNullRange = [&](const TypeCoupledDeclRefInfo &DepDeclInfo, + Expr *DepExp) { + if (DepDeclInfo.isDeref()) + return; + assert(isa(DepDeclInfo.getDecl())); + const auto *DepField = cast(DepDeclInfo.getDecl()); + assert(DepField->getFieldIndex() < IL->getNumInits()); + if (!isNullPtrInit(DepField->getFieldIndex(), SemaRef.Context)) { + SourceLocation Loc = IsImplicitInitExpr ? IL->getEndLoc() : Init->getExprLoc(); + SemaRef.Diag(Loc, diag::warn_bounds_safety_initlist_range_partial_null) + << IsImplicitInitExpr << FD << FD->getType() << DepExp; + } + }; + if (auto *EndPtr = OrigDRPTy->getEndPointer()) { + assert(isa(EndPtr->IgnoreParenCasts()) + || isa(EndPtr->IgnoreParenCasts())); + for (const auto &EndPtrDecl : OrigDRPTy->getEndPtrDecls()) { + diagnosePartialNullRange(EndPtrDecl, EndPtr); + } + } + if (auto *StartPtr = OrigDRPTy->getStartPointer()) { + assert(isa(StartPtr->IgnoreParenCasts()) + || isa(StartPtr->IgnoreParenCasts())); + for (const auto &StartPtrDecl : OrigDRPTy->getStartPtrDecls()) { + diagnosePartialNullRange(StartPtrDecl, StartPtr); + } + } + } + } + } + return HadError; +} + +bool diagnoseRecordInits(Sema &SemaRef, InitListExpr *IL, bool &NeedPreCheck, + bool DiagnoseAssignments = false) { + SmallVector FieldInitializersWithSideEffects; + bool Result = + diagnoseRecordInitsImpl(SemaRef, IL, NeedPreCheck, DiagnoseAssignments, + FieldInitializersWithSideEffects); + + // Avoid + // * Warning about structs that don't contain externally counted attributes. + // * Warning when we emitted errors. That just adds extra noise. + if (!NeedPreCheck || Result) + return Result; + + // This record needs bounds checks and there were no errors. Warn about any + // initializers that cause side effects. + for (auto *FE : FieldInitializersWithSideEffects) { + SemaRef.Diag(FE->getExprLoc(), diag::warn_bounds_safety_struct_init_side_effect) + << FE; + } + + return Result; +} + +} // namespace + +/// Recursively check Inits of InitListExpr and directly insert new AST nodes for pre-assignment checks and +/// necessary materializations. +/// +/// This currently doesn't support inits used in the checks to have side effects because this doesn't guarantees +/// that inits are evaluated in order when dependent fields and the other fields are mixed in an InitListExpr. +/// To support it, we need to make sure all inits including nested ones are materialized in the program order +/// before performing the assignment checks. +/// rdar://76568300 +/// +/// Similarly to assignment expressions, initialization list of structs with +/// dynamic count and/or dynamic count pointer fields needs run-time integrity +/// checks, e.g., the init list like following. +/// @code +/// struct S { int *__counted_by(len) ptr; int len; }; +/// struct S s = {wide_p, l}; +/// @endcode +/// This function tracks and records wide pointer expression assigned as an +/// element of init list so that CodeGen can insert necessary runtime integrity +/// checks for assigned dynamic count value, e.g., "len <= bound_of(wide_p)". +Expr *CheckCountAttributedDeclAssignments::HandleInitListExpr( + InitListExpr *IL, PreAssignCheck &Builder) { + if (IL->getType()->isRecordType()) { + auto RD = IL->getType()->getAsRecordDecl(); + if (RD->isUnion()) { + auto FD = IL->getInitializedFieldInUnion(); + if (FD && IL->getNumInits()) { + if (auto SubIL = dyn_cast(IL->getInit(0))) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(0, NewSubIL); + } + } + return nullptr; + } + llvm::SmallVector, 2> + CountPointerCheckPairs; + unsigned InitIndex = 0; + llvm::SmallVector MateredExprs; + for (auto FD : RD->fields()) { + assert(InitIndex < IL->getNumInits()); + unsigned CurrInitIndex = InitIndex; + Expr *Init = IL->getInit(InitIndex++); + + // Note: We don't handle CompoundLiteralExpr here which would contain + // a sub InitListExpr. Instead we rely on VisitCompoundLiteralExpr being + // called appropriately. + if (auto SubIL = dyn_cast(Init)) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(CurrInitIndex, NewSubIL); + continue; + } + + if (FD->hasAttr()) { + auto InitRes = Builder.getMaterializedValueIfNot(Init, &MateredExprs); + if (!(Init = InitRes.get())) + return nullptr; + Builder.insertDeclToNewValue(FD, Init); + } else if (FD->getType()->isCountAttributedType()) { + Expr *PtrExpr = findSourceExpr(Init); + auto PtrExprRes = + Builder.getMaterializedValueIfNot(PtrExpr, &MateredExprs); + if (!(PtrExpr = PtrExprRes.get())) + return nullptr; + + CountPointerCheckPairs.push_back( + std::make_pair(FD->getType(), PtrExpr)); + } else if (auto *OrigDRPTy = + FD->getType()->getAs()) { + Expr *PtrExpr = findSourceExpr(Init); + auto PtrExprRes = + Builder.getMaterializedValueIfNot(PtrExpr, &MateredExprs); + if (!(PtrExpr = PtrExprRes.get())) + return nullptr; + Builder.insertDeclToNewValue(FD, Init); + // The pointer that has an associated end pointer is responsible to do the bounds checks: + // lb(new_ptr) <= start <= new_ptr <= end <= ub(new_ptr) + // Thus, we check if the pointer has an end pointer. This is consistent with how we + // handle assignments (and this is a routine we handle init lists). + // XXX: This is based on the assumption that only '__ended_by' is exposed to the users + // and '__started_by' is an implicit attribute the compiler adds based on the corresponding + // '__ended_by'. We may revisit this if we decide to expose '__started_by' to users. + if (OrigDRPTy->getEndPointer()) { + CountPointerCheckPairs.push_back(std::make_pair(FD->getType(), PtrExpr)); + } + } + } + if (!CountPointerCheckPairs.empty()) { + ExprResult Result = Builder.build(CountPointerCheckPairs, IL, MateredExprs); + if (!Result.isInvalid()) + return Result.get(); + } + return nullptr; + } + + if (SemaRef.Context.getAsArrayType(IL->getType())) { + for (unsigned i = 0; i < IL->getNumInits(); ++i) { + if (auto SubIL = dyn_cast(IL->getInit(i))) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(i, NewSubIL); + } + } + } + return nullptr; +} + +bool CheckCountAttributedDeclAssignments::TraverseDeclStmt(DeclStmt *S) { + for (auto *I : S->decls()) { + auto Var = dyn_cast(I); + if (!Var) + return TraverseDecl(I); + + AssignedDeclRefResult Result; + analyzeVarDecl(SemaRef, Var, Result); + if (Result.IsFlexBase && Var->getInit()) { + RegisterDeclAssignment(nullptr, Result, /* IsSelfAssign */ false); + if (CurrentDepGroup->skipFlexCheck()) + return true; + + Expr *Init = Var->getInit(); + auto *CE = dyn_cast(Init); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast && + !Init->getType()->isPointerTypeWithBounds()) { + Init = CE->getSubExpr(); + } + if (Init->IgnoreParenCasts()->isNullPointerConstant( + SemaRef.Context, Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + CurrentDepGroup->setFlexBaseNull(); + CurrentDepGroup->setSkipFlexCheck(); + return true; + } + // -fbounds-safety doesn't check single to single casts. Likewise, + // we skip checks for single to single with flexible array member. + // The exception is when both the source and the destination are + // pointers to struct with flexible array member, because a single + // pointer to struct with flexible array member promotes to + // '__bidi_indexable' with the specified count value. + if (Init->getType()->isSinglePointerType()) { + assert(!Init->getType()->getAs() && + !Init->getType()->getAs()); + CurrentDepGroup->setSkipFlexCheck(); + return true; + } + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, Init); + CurrentDepGroup->insertMateredExpr(OVE); + ReplaceSubExpr(getParentMap(), OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertDeclToNewValue(Var, Var->getInit(), Result.Level); + } else if (Result.IsTrackedVar) { + InsertAssignedDecl(Result, /*IsSelfAssign*/false, /*VarDecl*/true); + if (Var->getType()->isBoundsAttributedType()) + HandleVarInit(Var); + else + TraverseStmt(Var->getInit()); + } else { + if (CurrentDepGroup && !CurrentDepGroup->isDeclInitBasedGroup()) + FinalizeDepGroup(); + TraverseDecl(I); + } + } + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitVarDecl(VarDecl *VD) { + auto InitExpr = VD->getInit(); + if (auto IL = dyn_cast_or_null(InitExpr)) { + // If VD is not local, the check should have already been done at earlier + // semantic analysis. + bool NeedPreCheck = false; + if (!diagnoseRecordInits(SemaRef, IL, NeedPreCheck) + && VD->hasLocalStorage() && NeedPreCheck) { + PreAssignCheck::DeclToNewValueTy DeclToNewValue; + PreAssignCheck Builder(SemaRef, getParentMap(), IL, DeclToNewValue); + if (auto *NewIL = HandleInitListExpr(IL, Builder)) { + if (VD->getInit() == InitExpr) { + VD->setInit(NewIL); + } else { + bool Mod = ReplaceSubExpr(getParentMap(), InitExpr, NewIL); + (void)Mod; + assert(Mod); + } + } + } + SetSideEffectAfter(); + } + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitCompoundLiteralExpr( + CompoundLiteralExpr *CLE) { + // Note this check prevents us from trying to transform already transformed + // CompoundLiteralExpr because the initializer list will be a + // `BoundsCheckExpr` after its transformed. + auto *ILE = llvm::dyn_cast_if_present(CLE->getInitializer()); + if (!ILE) + return true; + + // Avoid emitting duplicate diagnostics. There are at least two reasons why + // this can happen: + // + // 1. `diagnoseRecordInits` found an error so adding bounds checks was skipped + // and then this CompoundLiteralExpr is visited again. + // + // 2. New bounds checks are disabled. When this happens the ILE is not + // replaced with a BoundsCheckExpr which means this method won't return + // early if this CompoundLiteralExpr is visited again. + // + // + // The CFG form of the function being analyzed may contain the same ILE + // multiple times in different CFG statements which means the same + // CompoundLiteralExpr can be visited multiple times. + // + if (VisitedILEInCompoundLiteralExprs.contains(ILE)) { + return true; + } + VisitedILEInCompoundLiteralExprs.insert(ILE); + + // Skip emitting the bounds check if its not enabled + // rdar://110871666 + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + clang::LangOptionsBase::BS_CHK_CompoundLiteralInit)) + return true; + + // TODO: This diagnostic should always be enabled (i.e. should actually be + // called before we check for BS_CHK_CompoundLiteralInit being enabled) + // (rdar://138982703). However the diagnostics are currently guarded to avoid + // a build failure (rdar://137774167). + bool NeedPreCheck = false; + if (diagnoseRecordInits(SemaRef, ILE, NeedPreCheck, + /*DiagnoseAssignments=*/true) || + !NeedPreCheck) + return true; + + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), ILE, EmptyMap); + auto *NewILE = HandleInitListExpr(ILE, Builder); + if (!NewILE) + // Note in this case the ILE might still have changed but rather than the + // whole ILE being replaced one of the assignments might have changed. In + // this case the ILE will have been modified in-place so there is nothing + // left to do. + return true; + + // The ILE needs replacing + assert(CLE->getInitializer() == ILE && + "CLE initializer unexpectedly changed"); + CLE->setInitializer(NewILE); + getParentMap().setParent(NewILE, CLE); + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitCallExpr(CallExpr *E) { + if (CheckAndPopDelayedStmt(E)) + return true; + + if (InAssignToDepRHS) + PushDelayedStmt(E); + else + FinalizeDepGroup(); + + BaseVisitor::VisitCallExpr(E); + if (E->HasSideEffects(SemaRef.getASTContext(), + /*IncludePossibleEffects*/ true)) { + SetSideEffectAfter(); + } + return true; +} + +void CheckCountAttributedDeclAssignments::RegisterDeclAssignment( + Expr *AssignExpr, AssignedDeclRefResult &Result, bool IsSelfAssign) { + assert(Result.ThisVD); + if (!InsertAssignedDecl(Result, IsSelfAssign)) { + const Expr *PrevAssignExpr = CurrentDepGroup->getAssignExprForDecl(Result.ThisVD); + assert(PrevAssignExpr); + const unsigned DiagIndex = Result.IsRangePtrVar ? 2 + : (Result.IsOutCount || Result.IsCountVar) ? 0 + : 1; + SemaRef.Diag(PrevAssignExpr->getExprLoc(), + diag::err_bounds_safety_multiple_assignments_to_dynamic_bound_decl) + << DiagIndex << Result.ThisVD; + SemaRef.Diag(AssignExpr->getExprLoc(), diag::note_bounds_safety_previous_assignment); + } + UpdateLastAssign(AssignExpr, Result, IsSelfAssign); +} + +namespace { + +Expr *materializeLValue(ASTContext &Ctx, Expr *LV, + SmallVectorImpl &OVEs) { + Expr *Unwrapped = LV->IgnoreParenImpCasts(); + if (isa(Unwrapped)) + return LV; + + if (auto *ME = dyn_cast(Unwrapped)) { + if (!isa(ME->getBase())) { + OVEs.push_back(OpaqueValueExpr::Wrap(Ctx, ME->getBase())); + ME->setBase(OVEs.back()); + } + } + OVEs.push_back(OpaqueValueExpr::Wrap(Ctx, LV)); + return OVEs.back(); +} + +// A non-inout buf parameter having at least one dependent inout count parameter +// must be immutable. +void checkImplicitlyReadOnlyBuf(Sema &SemaRef, const Expr *E, + const AssignedDeclRefResult &Result, + bool IsSelfAssign = false) { + const ValueDecl *VD = Result.ThisVD; + if (IsSelfAssign || (!Result.IsCountPtrVar && !Result.IsRangePtrVar) || + !isa(VD) || isValueDeclOutBuf(VD)) { + return; + } + if (Result.IsCountPtrVar) { + const auto *DCPT = Result.Ty->getAs(); + assert(DCPT); + bool HasOutCount = std::any_of( + DCPT->dependent_decl_begin(), DCPT->dependent_decl_end(), + [](const auto &DI) { return isValueDeclOutCount(DI.getDecl()); }); + if (HasOutCount) { + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dynamic_count_pointer) + << VD->getName() << DCPT->getKind(); + } + return; + } + if (Result.IsRangePtrVar) { + const auto *DRPT = Result.Ty->getAs(); + assert(DRPT); + auto IsDIDeref = [](const TypeCoupledDeclRefInfo &DI) { + return DI.isDeref(); + }; + int DiagNo = -1; + if (std::any_of(DRPT->endptr_decl_begin(), DRPT->endptr_decl_end(), + IsDIDeref)) { + DiagNo = 0; + } else if (std::any_of(DRPT->startptr_decl_begin(), + DRPT->startptr_decl_end(), IsDIDeref)) { + DiagNo = 1; + } + + if (DiagNo >= 0) { + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dynamic_range_pointer) + << VD->getName() << DiagNo; + } + return; + } +} + +void checkImplicitlyReadOnlyDependentParamOfReturnType( + Sema &S, const Expr *E, const AssignedDeclRefResult &Result) { + bool IsDeref = Result.Level >= 1; + if (!Result.IsDependentParamOfReturnType || IsDeref) + return; + + // This should trigger an error, don't emit another one. + if (Result.IsOutCount) + return; + + // TODO: This diagnostic check should always be performed (rdar://138982703). + // The check is currently guarded to avoid potentially breaking the build. + if (!S.getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) + return; + + const ValueDecl *VD = Result.ThisVD; + const FunctionDecl *FD = cast(VD->getDeclContext()); + const auto *RetCATy = FD->getReturnType()->getAs(); + unsigned Kind = RetCATy ? RetCATy->getKind() : BoundsAttributedType::EndedBy; + S.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dependent_param_in_return) + << VD->getName() << Kind << FD->getName() << FD->getReturnType(); +} + +} // namespace + +bool CheckCountAttributedDeclAssignments::TraverseUnaryOperator( + UnaryOperator *E) { + if (!E->isIncrementDecrementOp()) + return BaseVisitor::TraverseUnaryOperator(E); + + AssignedDeclRefResult LHSResult; + Expr *SubExpr = E->getSubExpr(); + analyzeAssignedDeclRef(SemaRef, SubExpr, LHSResult); + + checkImplicitlyReadOnlyBuf(SemaRef, E, LHSResult); + checkImplicitlyReadOnlyDependentParamOfReturnType(SemaRef, E, LHSResult); + + // This check should be done in `checkArithmeticUnaryOpBoundsSafetyPointer`, + // however if the check is performed there then there will be false positives + // for pointer inc/dec in the lhs of assignments which aren't actually bounds + // checked due to rdar://98749526. So we do the check here so that we emit + // the diagnostic for the cases where bounds checks **are** inserted. + // TODO: Remove this (rdar://135833598). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + if (const auto *CATTy = SubExpr->getType()->getAs()) { + if (!DiagnosedPtrIncDec.contains(E) && + !SemaRef + .BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, SubExpr, E->isIncrementOp())) { + DiagnosedPtrIncDec.insert(E); + } + } + } + + if (LHSResult.IsTrackedVar) { + assert(!SubExpr->HasSideEffects(SemaRef.Context)); + RegisterDeclAssignment(E, LHSResult, /*SelfAssign*/false); + if (CurrentDepGroup->skipFlexCheck()) + return true; + + SmallVector OVEs; + auto *LHS = SubExpr; + if (!isa(SubExpr)) { + LHS = materializeLValue(SemaRef.Context, LHS, OVEs); + E->setSubExpr(LHS); + } + + SourceLocation Loc = E->getBeginLoc(); + QualType Ty = SubExpr->getType(); + llvm::APInt OneVal(/*bitwidth*/SemaRef.Context.getTypeSize(Ty), /*val*/1); + QualType IncDecTy = Ty->isIntegralOrEnumerationType() ? Ty : + SemaRef.Context.getIntTypeForBitwidth(OneVal.getBitWidth(), /*IsSigned*/false); + Expr* IncDecValue = IntegerLiteral::Create(SemaRef.Context, OneVal, IncDecTy, + Loc); + ExprResult NewBinOp = SemaRef.CreateBuiltinBinOp(Loc, + (E->isIncrementOp() ? BO_Add : BO_Sub), + LHS, + IncDecValue); + if (NewBinOp.isInvalid()) + return true; + OVEs.push_back(OpaqueValueExpr::Wrap(SemaRef.Context, NewBinOp.get())); + CurrentDepGroup->insertDeclToNewValue(LHSResult.ThisVD, OVEs.back(), + LHSResult.Level); + CurrentDepGroup->insertMateredExprsReverse(OVEs); + auto Res = TraverseStmt(SubExpr); + if (LHSResult.IsFlexBase) + SetSideEffectAfter(); + return Res; + } + return BaseVisitor::TraverseUnaryOperator(E); +} + +bool CheckCountAttributedDeclAssignments::TraverseBinaryOperator(BinaryOperator *E) { + if (E->isAssignmentOp()) { + AssignedDeclRefResult LHSResult; + analyzeAssignedDeclRef(SemaRef, E->getLHS(), LHSResult); + + // This check should be done in `checkArithmeticBinOpBoundsSafetyPointer`, + // however if the check is performed there then there will be false + // positives for pointer inc/dec in the lhs of assignments which aren't + // actually bounds checked due to rdar://98749526. So we do the check here + // so that we emit the diagnostic for the cases where bounds checks **are** + // inserted. + // TODO: Remove this (rdar://135833598). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + if (const auto *CATTy = + E->getLHS()->getType()->getAs()) { + switch (E->getOpcode()) { + case BO_AddAssign: // += + case BO_SubAssign: // -= + if (!DiagnosedPtrIncDec.contains(E) && + !SemaRef + .BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, E->getRHS(), E->getOpcode())) { + DiagnosedPtrIncDec.insert(E); + } + + break; + default: + // Don't try to emit the diagnostic for other operators + break; + } + } + } + + if (LHSResult.IsInnerStruct) { + auto *InnerStructDecl = cast(LHSResult.ThisVD); + auto *Att = InnerStructDecl->getAttr(); + assert(Att); + auto *RDecl = + InnerStructDecl->getType()->getAs()->getAsRecordDecl(); + assert(RDecl); + // Multiple structs with FAMs can refer to fields in the same struct, + // so the number can be >1 + assert(Att->dependerDecls_size() > 0); + for (auto DepDecl : Att->dependerDecls()) { + if (!LHSResult.FlexBaseDecl->isParentStructOf(DepDecl)) + continue; + ValueDecl *FAMDecl = cast(DepDecl); + auto *DCTy = FAMDecl->getType()->getAs(); + assert(DCTy); + for (auto Dep : DCTy->dependent_decls()) { + auto FieldD = dyn_cast(Dep.getDecl()); + if (!FieldD || !RDecl->isParentStructOf(FieldD)) + continue; + SemaRef.Diag(E->getOperatorLoc(), + diag::err_bounds_safety_dependent_struct_assignment) + << E->getLHS() << FieldD << FAMDecl; + SemaRef.Diag(DCTy->getCountExpr()->getBeginLoc(), + diag::note_bounds_safety_count_param_loc) + << DCTy->getCountExpr()->getSourceRange(); + return true; + } + llvm_unreachable( + "FAM refers to struct without referring to one of it's fields"); + } + } + + if (LHSResult.IsOutBuf) + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_dependent_out_count_buf_assign); + + if (LHSResult.IsOutCount) + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_dependent_out_count_assign); + + AssignedDeclRefResult RHSResult; + analyzeAssignedDeclRef(SemaRef, E->getRHS(), RHSResult); + // Allow self assignment + bool IsSelfAssign = LHSResult.IsTrackedVar && + (LHSResult.ThisVD == RHSResult.ThisVD) && + (LHSResult.Key == RHSResult.Key) && + (LHSResult.Level == RHSResult.Level); + + checkImplicitlyReadOnlyBuf(SemaRef, E, LHSResult, IsSelfAssign); + checkImplicitlyReadOnlyDependentParamOfReturnType(SemaRef, E, LHSResult); + + if (LHSResult.IsTrackedVar) + RegisterDeclAssignment(E, LHSResult, IsSelfAssign); + + SaveAndRestore InAssignToDepRHSLocal(InAssignToDepRHS, + LHSResult.IsTrackedVar); + + // We are only interested in the immediate wide pointer to dynamic + // bound pointer casts, rather than any casts happening during + // recursive traversal of subexpressions. + Expr *OrigRHS = E->getRHS(); + if (InAssignToDepRHS && !CurrentDepGroup->skipFlexCheck()) { + + // SelfAssign doesn't need additional recursive decl ref check, but + // it still needs materialization in HandleRHSOfAssignment. + if (!IsSelfAssign) + ProcessDeclRefInRHSRecursive(OrigRHS); + + Expr *NewValue = nullptr; + if (E->isCompoundAssignmentOp()) { + auto *LHS = E->getLHS(); + auto *RHS = E->getRHS(); + SmallVector OVEs; + // Evaluate RHS first. + if (!isa(RHS)) { + RHS = materializeLValue(SemaRef.Context, RHS, OVEs); + E->setRHS(RHS); + } + if (!isa(LHS)) { + LHS = materializeLValue(SemaRef.Context, LHS, OVEs); + E->setLHS(LHS); + } + BinaryOperator::Opcode AdjustedBinOp; + switch (E->getOpcode()) { + case BO_MulAssign: AdjustedBinOp = BO_Mul; break; + case BO_DivAssign: AdjustedBinOp = BO_Div; break; + case BO_RemAssign: AdjustedBinOp = BO_Rem; break; + case BO_AddAssign: AdjustedBinOp = BO_Add; break; + case BO_SubAssign: AdjustedBinOp = BO_Sub; break; + case BO_ShlAssign: AdjustedBinOp = BO_Shl; break; + case BO_ShrAssign: AdjustedBinOp = BO_Shr; break; + case BO_AndAssign: AdjustedBinOp = BO_And; break; + case BO_XorAssign: AdjustedBinOp = BO_Xor; break; + case BO_OrAssign: AdjustedBinOp = BO_Or; break; + default: llvm_unreachable("unknown compound operator!"); + } + ExprResult NewBinOp = SemaRef.CreateBuiltinBinOp( + E->getExprLoc(), AdjustedBinOp, LHS, RHS); + if (NewBinOp.isInvalid()) + return true; + NewValue = NewBinOp.get(); + assert(!isa(NewValue)); + OVEs.push_back(OpaqueValueExpr::Wrap(SemaRef.Context, NewValue)); + NewValue = OVEs.back(); + // Inserting OVE in a reversed order to match that the entire analysis + // is reversed. + CurrentDepGroup->insertMateredExprsReverse(OVEs); + } else if (LHSResult.IsFlexBase) { + Expr *RHS = OrigRHS; + CastExpr *E = dyn_cast(RHS); + if (E && E->getCastKind() == CK_BoundsSafetyPointerCast && + !OrigRHS->getType()->isPointerTypeWithBounds()) { + // This is a best effort to get an underlying wide pointer, but it may + // not be a wide pointer if there was no wide pointer in the first + // place and/or it is a null pointer. + RHS = E->getSubExpr(); + } + if (RHS->IgnoreParenCasts()->isNullPointerConstant( + SemaRef.Context, Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + CurrentDepGroup->setFlexBaseNull(); + CurrentDepGroup->setSkipFlexCheck(); + } else if (RHS->getType()->isSinglePointerType()) { + // -fbounds-safety doesn't check single to single casts. Likewise, + // we skip checks for single to single with flexible array member. + // The exception is when both the source and the destination are + // pointers to struct with flexible array member, because a single + // pointer to struct with flexible array member promotes to + // '__bidi_indexable' with the specified count value. + assert(!RHS->getType()->getAs() && + !RHS->getType()->getAs()); + CurrentDepGroup->setSkipFlexCheck(); + } + } + + if (!CurrentDepGroup->skipFlexCheck()) { + HandleRHSOfAssignment(E); + if (!NewValue) { + assert(E->isAssignmentOp()); + NewValue = E->getRHS(); + } + CurrentDepGroup->insertDeclToNewValue(LHSResult.ThisVD, NewValue, + LHSResult.Level); + } + } + // We still traverse RHS to detect possible side effects. + if (!IsSelfAssign) + TraverseStmt(OrigRHS); + if (!LHSResult.IsTrackedVar && + SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) + TraverseStmt(E->getLHS()); + + if (LHSResult.IsFlexBase) + SetSideEffectAfter(); + + return true; + } else if (E->isCommaOp()) { + // Skip traversing the operands of comma operators here because the operands + // will be visited as seperate statements. Without skipping, the same + // expressions were traversed twice and this produced unstructured OVEs and + // led to an assertion failure in Clang CodeGen. For example, the block for + // `p++, len--` should look like this. The code here skips (3) and the + // traversals are done for (1) and (2). [B1] + // 1: p++ + // 2: len-- + // 3: [B1.1] , [B1.2] <- comma operator + return false; + } + return BaseVisitor::TraverseBinaryOperator(E); +} + +void CheckCountAttributedDeclAssignments::ProcessDeclRefInRHSRecursive(Expr *E) { + assert(CurrentDepGroup != nullptr); + struct DeclRefVisitor : public RecursiveASTVisitor { + Sema &SemaRef; + DepGroup &DepGroupRef; + + DeclRefVisitor(Sema &SemaRef, DepGroup &DepGroupRef) + : SemaRef(SemaRef), DepGroupRef(DepGroupRef) {} + + bool VisitDeclRefExpr(DeclRefExpr *E) { + addReferencedDecl(E); + return true; + } + bool VisitMemberExpr(MemberExpr *E) { + addReferencedDecl(E); + return true; + } + + bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) { + return TraverseStmt(E->getSourceExpr()); + } + + bool TraverseBoundsCheckExpr(BoundsCheckExpr *E) { + return TraverseStmt(E->getGuardedExpr()); + } + + bool TraverseMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + return TraverseStmt(E->getWrappedExpr()); + } + + bool TraverseBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + return TraverseStmt(E->getSubExpr()); + } + + void addReferencedDecl(Expr *E) { + AssignedDeclRefResult Result; + analyzeAssignedDeclRef(SemaRef, E, Result); + if (Result.IsTrackedVar && + DepGroupRef.matchKey(Result.Key, Result.ThisVD)) { + DepGroupRef.addReferencedDecl(Result.ThisVD, Result.Level); + } + } + } DeclRefVisitorInst{SemaRef, *CurrentDepGroup}; + DeclRefVisitorInst.TraverseStmt(E); +} + +void CheckCountAttributedDeclAssignments::HandleRHSOfAssignment(BinaryOperator *AssignOp) { + assert(InAssignToDepRHS); + ParentMap &PM = getParentMap(); + Expr *RHS = AssignOp->getRHS()->IgnoreParens(); + CastExpr *E = dyn_cast(RHS); + if (!E || E->getCastKind() != CK_BoundsSafetyPointerCast) { + if (!isa(AssignOp->getRHS())) { + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, AssignOp->getRHS()); + PM.setParent(OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertMateredExpr(OVE); + AssignOp->setRHS(OVE); + PM.setParent(OVE, AssignOp); + } + return; + } + + if (!isa(E->getSubExpr())) { + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E->getSubExpr()); + PM.setParent(OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertMateredExpr(OVE); + E->setSubExpr(OVE); + PM.setParent(OVE, E); + } + return; +} + +void CheckCountAttributedDeclAssignments::HandleVarInit(VarDecl *VD) { + if (!VD->getInit() || !VD->hasLocalStorage()) + return; + Expr *Init = VD->getInit()->IgnoreParens(); + + // For variable initialization, we can just emit a check immediately because + // the length variable has necessarily been initialized before (else it + // couldn't have been referenced in the dynamic bound pointer type). + // XXX: this will not work for DynamicRangePointerType. + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), VD->getInit(), EmptyMap); + + CastExpr *E = dyn_cast(Init); + if (E && E->getType()->isBoundsAttributedType()) { + Init = E->getSubExpr(); + } + ExprResult Result = Builder.build(VD->getType(), Init); + if (Result.isInvalid()) + return; + + auto Res = Result.get(); + if (Res->getType()->isPointerTypeWithBounds()) { + assert(VD->getType()->isBoundsAttributedType()); + Res = SemaRef + .ImpCastExprToType(Res, VD->getType(), + CastKind::CK_BoundsSafetyPointerCast) + .get(); + } + VD->setInit(Res); +} + +// Emit a bounds check if the function returns bounds-attributed pointer type. +// The bounds check will load all dependent decls and verify if the dynamic +// bound pointer has enough bytes. +bool CheckCountAttributedDeclAssignments::TraverseReturnStmt(ReturnStmt *S) { + const auto *FD = cast(AC.getDecl()); + + QualType RetTy = FD->getReturnType(); + const auto *RetBATy = RetTy->getAs(); + if (!RetBATy) + return BaseVisitor::TraverseReturnStmt(S); + + auto *RetVal = S->getRetValue(); + if (!RetVal || RetVal->containsErrors()) { + // This should have been diagnosed earlier. + return true; + } + + // Return size checks are hidden behind a flag. + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + return BaseVisitor::TraverseReturnStmt(S); + } + + // Remove the cast to return type (bounds attribute type) that can be added by + // Sema. This results in a more readable AST because + // the pointer used in bounds checks doesn't have the bounds attribute. E.g. + // having the `__counted_by` attribute on a pointer used in bounds checks + // before it's been checked is confusing. + auto *ICE = dyn_cast(RetVal); + if (ICE && RetVal->getType()->isBoundsAttributedType()) + RetVal = ICE->getSubExpr(); + + SmallVector OVEs; + + // Wrap the return value into OVE to evaluate the value only once. + auto *RetOVE = OpaqueValueExpr::Wrap(SemaRef.Context, RetVal); + OVEs.push_back(RetOVE); + + // Add implicit cast to the guarded expression so that it matches the return + // type. This makes the AST more readable but isn't necessarily required by + // codegen. + CastKind Kind = CK_BoundsSafetyPointerCast; + if (!RetVal->getType()->isPointerType()) { + assert(RetVal->isNullPointerConstant(SemaRef.getASTContext(), + Expr::NPC_NeverValueDependent)); + Kind = CK_NullToPointer; + } + ExprResult GuardedRes = SemaRef.ImpCastExprToType(RetOVE, RetTy, Kind); + if (GuardedRes.isInvalid()) + return true; + + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), GuardedRes.get(), EmptyMap); + + // Wrap all dependent decls into OVEs, since the bounds check for __ended_by() + // uses the end pointer value multiple times. + for (const auto &DI : RetBATy->dependent_decls()) { + ValueDecl *VD = DI.getDecl(); + + // The decl should be either ParmVarDecl or VarDecl that has + // __unsafe_late_const attribute. + assert(isa(VD)); + + // Load the decl. + DeclarationNameInfo NameInfo(VD->getDeclName(), RetVal->getBeginLoc()); + auto *DRE = DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), + /*TemplateKWLoc=*/SourceLocation(), VD, + /*RefersToCapturedVariable=*/false, + NameInfo, VD->getType(), VK_LValue); + auto Res = SemaRef.DefaultLvalueConversion(DRE); + if (Res.isInvalid()) + return true; + Expr *E = Res.get(); + + if (DI.isDeref()) { + // Dereference the loaded pointer. + auto Res = + SemaRef.CreateBuiltinUnaryOp(RetVal->getBeginLoc(), UO_Deref, E); + if (Res.isInvalid()) + return true; + E = Res.get(); + } + + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E); + OVEs.push_back(OVE); + + unsigned Level = DI.isDeref() ? 1 : 0; + Builder.insertDeclToNewValue(VD, OVE, Level); + } + + auto Res = Builder.build(RetTy, RetOVE, /*WrappedValue=*/nullptr, &OVEs); + if (Res.isInvalid()) + return true; + + S->setRetValue(Res.get()); + getParentMap().setParent(Res.get(), S); + return false; +} + +/// checkCountAttributedLocalsInBlock - This checks if dependent local variables +/// are declared within the same basic block. +bool checkCountAttributedLocalsInBlock(Sema &SemaRef, const CFGBlock *block) { + llvm::SmallPtrSet LocalDeclSet; + bool HadError = false; + + auto checkLocalDependentCountTypes = [&](const VarDecl *VD) { + QualType Ty = VD->getType(); + while (!Ty.isNull()) { + if (auto DCPTy = Ty->getAs()) { + for (const TypeCoupledDeclRefInfo &DI : DCPTy->dependent_decls()) { + if (IsModifiableValue(SemaRef, DI.getDecl()) && + LocalDeclSet.find(DI.getDecl()) == LocalDeclSet.end()) { + // The dependent decl can still be non-local variable if there was + // an erroneous condition. Skip it to avoid reporting an irrelevant + // error message. + if (auto *VD = dyn_cast(DI.getDecl())) { + if (!VD->isLocalVarDecl()) { + assert(SemaRef.hasUncompilableErrorOccurred()); + continue; + } + SemaRef.Diag(VD->getLocation(), + diag::err_bounds_safety_local_dependent_count_block); + HadError = true; + } + } + } + } + Ty = Ty->getPointeeType(); + } + }; + + for (CFGBlock::const_iterator BI = block->begin(), BE = block->end(); + BI != BE; ++BI) { + if (std::optional stmt = BI->getAs()) { + const DeclStmt *DS = dyn_cast(stmt->getStmt()); + if (!DS) + continue; + for (auto D : DS->decls()) { + auto VD = dyn_cast(D); + if (!VD) + continue; + checkLocalDependentCountTypes(VD); + LocalDeclSet.insert(VD); + } + } + } + return !HadError; +} + +namespace clang { + +void DynamicCountPointerAssignmentAnalysis::run() { + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, dcl); + + CFG *cfg = AC.getCFG(); + if (!cfg) + return; + + for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + const CFGBlock *block = *I; + checkCountAttributedLocalsInBlock(SemaRef, block); + CheckCountAttributedDeclAssignments checkDependents(SemaRef, AC); + /// Visiting Stmt within a CFGBlock backward. Doing so makes easier to + /// determine whether a function call is used in RHS of a tracked assignment + /// expression or the call is standalone. + for (CFGBlock::const_reverse_iterator BI = (*I)->rbegin(), + BE = (*I)->rend(); + BI != BE; ++BI) { + if (std::optional stmt = BI->getAs()) + checkDependents.BeginStmt(const_cast(stmt->getStmt())); + } + } +} + +RecordDecl *FlexibleArrayMemberUtils::GetFlexibleRecord(QualType QT) { + auto *RT = QT->getAs(); + if (!RT) + return nullptr; + + auto *RD = RT->getDecl(); + if (!RD->hasFlexibleArrayMember()) + return nullptr; + + const CountAttributedType *DCPTy = nullptr; + RecordDecl *FlexibleRecord = RD; + do { + if (FlexibleRecord->getTagKind() == TagTypeKind::Union) + return nullptr; + FieldDecl *FlexibleField = nullptr; + for (FieldDecl *FD : FlexibleRecord->fields()) { + FlexibleField = FD; + } + assert(FlexibleField); + QualType FieldType = FlexibleField->getType(); + if (auto *RT = FieldType->getAs()) { + FlexibleRecord = RT->getDecl(); + } else { + DCPTy = FieldType->isIncompleteArrayType() ? FieldType->getAs() : nullptr; + FlexibleRecord = nullptr; + } + } while (!DCPTy && FlexibleRecord); + + if (!DCPTy) + return nullptr; + + return RD; +} + +bool FlexibleArrayMemberUtils::Find( + RecordDecl *RD, SmallVectorImpl &PathToFlex, + ArrayRef &CountDecls) { + if (!RD->hasFlexibleArrayMember()) + return false; + + const CountAttributedType *DCPTy = nullptr; + RecordDecl *FlexibleRecord = RD; + do { + if (FlexibleRecord->getTagKind() == TagTypeKind::Union) + return false; + FieldDecl *FlexibleField = nullptr; + for (FieldDecl *FD : FlexibleRecord->fields()) { + FlexibleField = FD; + } + assert(FlexibleField); + PathToFlex.push_back(FlexibleField); + QualType FieldType = FlexibleField->getType(); + if (auto *RT = FieldType->getAs()) { + FlexibleRecord = RT->getDecl(); + } else { + DCPTy = FieldType->isIncompleteArrayType() ? FieldType->getAs() : nullptr; + FlexibleRecord = nullptr; + } + } while (!DCPTy && FlexibleRecord); + + if (!DCPTy) + return false; + + CountDecls = DCPTy->getCoupledDecls(); + return true; +} + +Expr *FlexibleArrayMemberUtils::SelectFlexibleObject( + const SmallVectorImpl &PathToFlex, Expr *Base) { + bool IsArrow = Base->getType()->isPointerType(); + Expr *FlexibleObj = Base; + for (unsigned I = 0; I < PathToFlex.size() - 1; ++I) { + if (IsArrow && !FlexibleObj->isPRValue()) { + FlexibleObj = ImplicitCastExpr::Create( + SemaRef.Context, FlexibleObj->getType(), CK_LValueToRValue, + FlexibleObj, nullptr, VK_PRValue, SemaRef.CurFPFeatureOverrides()); + } + auto *FlexibleField = PathToFlex[I]; + FlexibleObj = MemberExpr::CreateImplicit( + SemaRef.Context, FlexibleObj, IsArrow, FlexibleField, + FlexibleField->getType(), VK_LValue, OK_Ordinary); + IsArrow = false; + } + return FlexibleObj; +} + +ExprResult FlexibleArrayMemberUtils::BuildCountExpr( + FieldDecl *FlexibleField, const ArrayRef CountDecls, + Expr *StructBase, SmallVectorImpl &OVEs, + CopyExpr *DeclReplacer) { + QualType FlexType = FlexibleField->getType(); + const auto *DCPTy = FlexType->getAs(); + assert(DCPTy); + Expr *CountTemplate = DCPTy->getCountExpr(); + + CopyExpr Copy = DeclReplacer ? *DeclReplacer : CopyExpr(SemaRef); + CopyExpr CopyNoRepl(SemaRef); + bool IsArrow = StructBase->getType()->isPointerType(); + for (auto DeclRefInfo : CountDecls) { + auto *VD = DeclRefInfo.getDecl(); + // For `counted_by(hdr.len)` we don't need a replacement for the MemberExpr + // (`.len`), only the DeclRefExpr (`hdr`). + // For `counted_by(len + len)` we don't need to add `len` twice - the full + // count expression is transformed outside the loop and will replace both + // DeclRefs regardless. + if (DeclRefInfo.isMember() || Copy.HasDeclReplacement(VD)) + continue; + + if (auto *FD = dyn_cast(VD)) { + Expr *Base = CopyNoRepl.TransformExpr(StructBase).get(); + Base = SemaRef.DefaultLvalueConversion(Base).get(); + if (IsArrow && !Base->isPRValue()) { + Base = ImplicitCastExpr::Create( + SemaRef.Context, Base->getType(), CK_LValueToRValue, Base, nullptr, + VK_PRValue, SemaRef.CurFPFeatureOverrides()); + } + ExprObjectKind OK = FD->isBitField() ? OK_BitField : OK_Ordinary; + auto *NewMember = MemberExpr::CreateImplicit( + SemaRef.Context, Base, IsArrow, FD, FD->getType(), VK_LValue, OK); + ExprResult Lvalue = SemaRef.DefaultLvalueConversion(NewMember); + if (!Lvalue.get()) + return ExprError(); + + // Don't wrap the count member access in OVE. Instead, the user of + // this expression should make sure it's always rebuilt. The base pointer + // may be null so we guard member access to the base in the null check. + // However, a materialization expression that wraps the null check may + // still evaluate the member access, To avoid this, we do not wrap the + // count member access in OVE. + Copy.UnsafelyAddDeclSubstitution(FD, Lvalue.get()); + } + } + + ExprResult Transformed = Copy.TransformExpr(CountTemplate); + if (!Transformed.get()) + return ExprError(); + + return SemaRef.DefaultLvalueConversion(Transformed.get()); +} + +TransformDynamicCountWithFunctionArgument :: + TransformDynamicCountWithFunctionArgument( + Sema &SemaRef, const SmallVectorImpl &Args, unsigned FirstParam) + : BaseTransform(SemaRef), ActualArgs(Args), FirstParam(FirstParam) {} + +ExprResult TransformDynamicCountWithFunctionArgument::TransformDeclRefExpr( + DeclRefExpr *E) { + ParmVarDecl *FormalArg = dyn_cast(E->getDecl()); + if (!FormalArg) + return Owned(E); + + unsigned Index = FormalArg->getFunctionScopeIndex(); + assert(Index >= FirstParam); + Index -= FirstParam; + assert(Index < ActualArgs.size()); + return ActualArgs[Index]; +} + +} // namespace clang diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h new file mode 100644 index 0000000000000..d9272d445f189 --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h @@ -0,0 +1,396 @@ +//===--- DynamicCountPointerAssignmentAnalysis.h --------------------------===// +// +// 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 interface of dynamic count pointer assignment analysis +// for -fbounds-safety. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H +#define LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H + +#include "TreeTransform.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/SaveAndRestore.h" + +namespace clang { + +class DynamicCountPointerAssignmentAnalysis { + Sema &SemaRef; + Decl *dcl; +public: + DynamicCountPointerAssignmentAnalysis(Sema &SemaRef, Decl *dcl) + : SemaRef(SemaRef), dcl(dcl) {} + + void run(); + + static RecordDecl *computeFlexBaseKey(Expr *InE, + llvm::raw_string_ostream *OS); +}; + +/// CopyExpr simply duplicates expressions up to OpaqueValueExpr, which are +/// used as-is. +class CopyExpr : public TreeTransform { + /// List of declarations that should be substituted for a given expression. + /// This is meant to assist with bounds checking on CountAttributedType + /// by allowing users to replace the reference declaration with the right + /// expression. + llvm::SmallVector, 1> Replacements; + bool InSyntacticTransformation = false; + + ValueDecl *SelectFirstDecl(ValueDecl *Decl) { + if (auto *VD = dyn_cast(Decl)) { + Decl = VD->getFirstDecl(); + } else if (auto *FD = dyn_cast(Decl)) { + Decl = FD->getFirstDecl(); + } + return Decl; + } +public: + using TreeTransform::TreeTransform; + + bool AlwaysRebuild() { return true; } + + void AddDeclSubstitution(ValueDecl *DeclToChange, + OpaqueValueExpr *ValueToUse) { + UnsafelyAddDeclSubstitution(DeclToChange, ValueToUse); + } + + // UnsafelyAddDeclSubstitution lets you replace declaration references with + // any expression that you like better, including expressions that would + // unsafely be used in multiple places in the AST. Prefer AddDeclSubstitution + // unless you expect the result to not actually be used in the final AST. + void UnsafelyAddDeclSubstitution(ValueDecl *DeclToChange, Expr *ValueToUse) { + Replacements.emplace_back(SelectFirstDecl(DeclToChange), ValueToUse); + } + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E)) + return TreeTransform::TransformExpr(E->getSourceExpr()); + // Don't copy expressions inside opaque values. + return E; + } + + bool HasDeclReplacement(ValueDecl *VD) { + for (auto &Pair : Replacements) { + if (Pair.first == VD) { + return true; + } + } + return false; + } + + ExprResult RebuildDeclRefExpr(NestedNameSpecifierLoc QualifierLoc, + ValueDecl *VD, + const DeclarationNameInfo &NameInfo, + NamedDecl *Found, + TemplateArgumentListInfo *TemplateArgs) { + VD = SelectFirstDecl(VD); + for (auto &Pair : Replacements) { + if (Pair.first == VD) { + // Rebuild the replacement expression. To avoid recursion, we + // use a new instance of CopyExpr. + return CopyExpr(getSema()).TransformExpr(Pair.second); + } + } + + // TreeTransform::RebuildDeclRefExpr below does semantic analysis, and + // well-formed programs cannot have references to fields in C. However, in + // -fbounds-safety we can define a struct: + // struct { + // void *p1; + // void *__ended_by(p1) p2; + // void *__ended_by(p2-1) p3; + // }; + // p2 in p2-1 will be bounds-safety-promoted to __bidi_indexable during + // LValueToRValue. This will try to create copies of DeclRefExprs to p1 and + // p2, which would trigger an assert "building reference to field in C?' in + // TreeTransform::RebuildDeclRefExpr. To avoid this, we build the + // DeclRefExprs here manually. + if (isa(VD)) { + // Copy-paste from Sema::BuildDeclarationNameExpr, but without assert. + QualType Ty = VD->getType().getNonReferenceType(); + ExprValueKind VK = VK_LValue; + CXXScopeSpec SS; + SS.Adopt(QualifierLoc); + return getSema().BuildDeclRefExpr(VD, Ty, VK, NameInfo, &SS, Found, + /*TemplateKWLoc=*/SourceLocation(), + TemplateArgs); + } + + return TreeTransform::RebuildDeclRefExpr(QualifierLoc, VD, NameInfo, Found, + TemplateArgs); + } + + /// Super class returns the syntactic form only. Override it to ensure that + /// TransformMemberExpr can access the semantic form. + ExprResult TransformInitListExpr(InitListExpr *E) { + if (InSyntacticTransformation) + return TreeTransform::TransformInitListExpr(E); + assert(E->isSemanticForm()); + bool InitChanged = false; + + EnterExpressionEvaluationContext Context( + getSema(), EnterExpressionEvaluationContext::InitList); + + SmallVector Inits; + if (TransformExprs(E->getInits(), E->getNumInits(), false, Inits, + &InitChanged)) + return ExprError(); + + ExprResult Res = + RebuildInitList(E->getLBraceLoc(), Inits, E->getRBraceLoc()); + if (Res.isUsable()) { + SaveAndRestore SAR(InSyntacticTransformation, true); + ExprResult SyntacticRes = TreeTransform::TransformInitListExpr(E); + if (SyntacticRes.isUsable()) { + auto SyntacticIL = cast(SyntacticRes.get()); + assert(SyntacticIL->isSyntacticForm()); + cast(Res.get())->setSyntacticForm(SyntacticIL); + } + } + return Res; + } + + ExprResult RebuildMemberExpr(Expr *Base, SourceLocation OpLoc, + bool isArrow, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, + const DeclarationNameInfo &MemberNameInfo, + ValueDecl *Member, + NamedDecl *FoundDecl, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + NamedDecl *FirstQualifierInScope) { + if (auto IL = dyn_cast(Base); + IL && !InSyntacticTransformation) { + // We need field initializers to align with the struct type. + assert(IL->isSemanticForm()); + auto *VD = cast(Member); + Expr *Val = IL->getInitForField(VD); + assert(Val && !isa(Val)); + return Val; + } + + // XXX: this assumes that you'll only see the field accessed on the + // specific (and implicit) object you're interested in. It's not a problem + // with the current user-facing rules, which prohibit count expressions + // from referencing a field off of any other object, but if the rules + // change or the compiler starts synthesizing expressions that do so, this + // would need to change in difficult ways. + for (auto &Pair : Replacements) { + if (Pair.first == Member) { + // Rebuild the replacement expression. To avoid recursion, we + // use a new instance of CopyExpr. + return CopyExpr(getSema()).TransformExpr(Pair.second); + } + } + return TreeTransform::RebuildMemberExpr( + Base, OpLoc, isArrow, QualifierLoc, TemplateKWLoc, MemberNameInfo, + Member, FoundDecl, ExplicitTemplateArgs, FirstQualifierInScope); + } +}; + +class ForceRebuild : public TreeTransform { +public: + using BaseTransform = TreeTransform; + bool AlwaysRebuild() { return true; } + ForceRebuild(Sema &SemaRef) : TreeTransform(SemaRef) {} + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E)) + return TransformExpr(E->getSourceExpr()); + return E; + } + + // Workaround upstream behavior of TreeTransform. + // + // Normally rebuilding `CompoundLiteralExpr` runs + // `getSema().BuildCompoundLiteralExpr` which reruns Sema checks. That + // function checks `CurContext->isFunctionOrMethod();` which won't necessarily + // get the Context this CompoundLiteralExpr was originally built in which + // means we can emit spurious `diag::err_init_element_not_constant` + // diagnostics. To workaround this we construct the expression directly using + // the information on `CompoundLiteralExpr::isFileScope()`. + // + // FIXME: Is this a bug upstream or intentional behavior? + ExprResult RebuildCompoundLiteralExpr(SourceLocation LParenLoc, + TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *Init, + bool IsFileScope) { + + QualType literalType = TInfo->getType(); + // Copied from Sema::BuildCompoundLiteralExpr + ExprValueKind VK = (getSema().getLangOpts().CPlusPlus && + !(IsFileScope && literalType->isArrayType())) + ? VK_PRValue + : VK_LValue; + + auto *E = new (getSema().getASTContext()) CompoundLiteralExpr( + LParenLoc, TInfo, TInfo->getType(), VK, Init, IsFileScope); + return E; + } +}; + +/// Create a new expression in terms of pre-registered RHS of assignments by +/// replacing a DeclRef or MemberExpr with corresponding RHS. +/// +/// We are performing checks before any of the assignments are done. This means, +/// we can't simply load the updated \c f->cnt to perform count checks like this +/// \f[0 <= f->cnt + 2 <= bounds_of(new_ptr)\f] since we only have the old \c +/// f->cnt. Therefore, we create a new count expression in terms of \c new_cnt +/// by replacing \c f->cnt, which is \c new_cnt + 2 in the example. +/// +/// \code +/// struct Foo { int *__counted_by(cnt + 2) ptr; int cnt; }; +/// void Test(struct Foo *f) { +/// f->ptr = new_ptr; +/// f->cnt = new_cnt; +/// \endcode +struct ReplaceDeclRefWithRHS : public TreeTransform { + using BaseTransform = TreeTransform; + using ExprLevelPair = std::pair; + using DeclToNewValueTy = llvm::DenseMap; + + llvm::SmallPtrSet ReplacingValues; + unsigned LevelToUnwrap = 0; + DeclToNewValueTy &DeclToNewValue; + Expr *MemberBase = nullptr; + + explicit ReplaceDeclRefWithRHS(Sema &SemaRef, + DeclToNewValueTy &DeclToNewValue) + : BaseTransform(SemaRef), DeclToNewValue(DeclToNewValue) {} + + bool AlwaysRebuild() { return true; } + + ExprResult TransformBoundsAttrExpr(Expr *E) { + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + nullptr, ExpressionKind::EK_AttrArgument); + return TransformExpr(E); + } + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { return Owned(E); } + + /// If we are replacing the value pointed to by DeclRef, we unwrap dereference + /// operator(s) from the original bound expression. + /// + /// In the following example, we replace \c *out_cnt the argument of \c + /// __counted_by with \c new_cnt, not just \c out_cnt. + /// + /// \code + /// void foo(int *__counted_by(*out_cnt) *out_buf, int *out_cnt) { + /// *out_buf = new_buf; + /// *out_cnt = new_cnt; + /// } + /// \endcode + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() != UO_Deref) + return BaseTransform::TransformUnaryOperator(E); + + ExprResult SubExpr = TransformExpr(E->getSubExpr()); + if (SubExpr.isInvalid()) + return ExprError(); + + if (LevelToUnwrap != 0) { + --LevelToUnwrap; + return SubExpr; + } + + return RebuildUnaryOperator(E->getOperatorLoc(), E->getOpcode(), + SubExpr.get()); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + auto It = DeclToNewValue.find(E->getDecl()); + if (It == DeclToNewValue.end()) { + if (auto *Field = dyn_cast(E->getDecl())) { + assert(MemberBase); + bool IsArrow = MemberBase->getType()->isPointerType(); + return MemberExpr::CreateImplicit(getSema().Context, MemberBase, + IsArrow, Field, Field->getType(), + VK_LValue, OK_Ordinary); + } + return BaseTransform::TransformDeclRefExpr(E); + } + // Clone the new value. + return Replace(It->second.first, It->second.second); + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + auto It = DeclToNewValue.find(E->getMemberDecl()); + if (It == DeclToNewValue.end()) + return BaseTransform::TransformMemberExpr(E); + return Replace(It->second.first, It->second.second); + } + + ExprResult Replace(Expr *New, unsigned Level) { + ExprResult Repl = ForceRebuild(SemaRef).TransformExpr(New); + + if (const Expr *E = Repl.get()) + ReplacingValues.insert(E); + + assert(LevelToUnwrap == 0); + LevelToUnwrap = Level; + return Repl; + } + + const llvm::SmallPtrSetImpl &GetReplacingValues() const { + return ReplacingValues; + } +}; + +class FlexibleArrayMemberUtils { + Sema &SemaRef; + +public: + FlexibleArrayMemberUtils(Sema &SemaRef) : SemaRef(SemaRef) + { } + + /// If RD is not a union and has a flexible array member with a dynamic count, + /// return true and fill PathToFlex with every field decl towards the flexible + /// field, and CountDecls with the list of declarations involved in the count + /// expression. Return false otherwise. + bool Find(RecordDecl *RD, llvm::SmallVectorImpl &PathToFlex, + ArrayRef &CountDecls); + + RecordDecl *GetFlexibleRecord(QualType QT); + + Expr *SelectFlexibleObject( + const llvm::SmallVectorImpl &PathToFlex, Expr *Base); + + ExprResult BuildCountExpr(FieldDecl *FlexibleField, + const ArrayRef CountDecls, + Expr *Base, + llvm::SmallVectorImpl &OVEs, + CopyExpr *Copy = nullptr); +}; + +/// Function return type or argument may have a '__counted_by()' attribute with +/// the count expression expressed with ParmVarDecl. Similarly to instantiating +/// count expression of struct fields expressed by FieldDecl, we replace +/// DeclRefExpr of ParmVarDecl with the actual argument of the function call. +class TransformDynamicCountWithFunctionArgument + : public TreeTransform { + typedef TreeTransform + BaseTransform; + const SmallVectorImpl &ActualArgs; + unsigned FirstParam = 0; + +public: + TransformDynamicCountWithFunctionArgument(Sema &SemaRef, + const SmallVectorImpl &Args, + unsigned FirstParam = 0); + + ExprResult TransformDeclRefExpr(DeclRefExpr *E); +}; + +} // end namespace clang + +#endif // LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp new file mode 100644 index 0000000000000..20d93c5b0bb99 --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp @@ -0,0 +1,22 @@ +#include "clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h" +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" +#include "clang/AST/Expr.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +ExprResult ReplaceCountExprParamsWithArgsFromCall(const Expr *CountExpr, + const CallExpr *CE, Sema &S) { + // FIXME: Use of `const_cast` is here because it is hard to make + // TreeTransform work with `const Expr` but we also want to provide a sane + // public interface. + CallExpr *CENoConst = const_cast(CE); + SmallVector CallArgs( + CENoConst->getArgs(), CENoConst->getArgs() + CENoConst->getNumArgs()); + TransformDynamicCountWithFunctionArgument T(S, CallArgs); + ExprResult Replaced = T.TransformExpr(const_cast(CountExpr)); + return Replaced; +} +} // namespace clang diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index e2aadd5aa74b6..56ab23cb65f6a 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -157,11 +157,44 @@ namespace clang { namespace sema { class SemaPPCallbacks : public PPCallbacks { + /*TO_UPSTREAM(BoundsSafety) ON*/ + struct Include { + SourceLocation FileLoc; + BoundsSafetyPointerAttributes FAttr; + + Include(SourceLocation FileLoc) : FileLoc(FileLoc) {} + }; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Sema *S = nullptr; - llvm::SmallVector IncludeStack; + // TO_UPSTREAM(BoundsSafety): SourceLocation -> Include + llvm::SmallVector IncludeStack; llvm::SmallVector ProfilerStack; + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyPointerAttributes RootFAttr; + + /*TO_UPSTREAM(BoundsSafety) ON*/ + void PushInclude(SourceLocation IncludeLoc, SourceLocation FileLoc) { + IncludeStack.push_back(Include(IncludeLoc)); + if (S->getSourceManager().isInSystemHeader(FileLoc)) { + IncludeStack.back().FAttr = SystemHeaderAttributes(); + } else { + IncludeStack.back().FAttr = S->CurPointerAbi; + } + } + + SourceLocation PopInclude() { + return IncludeStack.pop_back_val().FileLoc; + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ public: + /*TO_UPSTREAM(BoundsSafety) ON*/ + SemaPPCallbacks() { + RootFAttr.setSingle(); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + void set(Sema &S) { this->S = &S; } void reset() { S = nullptr; } @@ -174,15 +207,26 @@ class SemaPPCallbacks : public PPCallbacks { switch (Reason) { case EnterFile: { SourceManager &SM = S->getSourceManager(); - SourceLocation IncludeLoc = SM.getIncludeLoc(SM.getFileID(Loc)); + FileID FID = SM.getFileID(Loc); + SourceLocation IncludeLoc = SM.getIncludeLoc(FID); + /* TO_UPSTREAM(BoundsSafety) ON*/ + StringRef Filename(""); + // IncludeLoc may be invalid because of #line pragma. Retry with `PresumedLoc` which contains the location modified by #line. + if (IncludeLoc.isInvalid()) { + auto PresumedLoc = SM.getPresumedLoc(Loc); + IncludeLoc = PresumedLoc.getIncludeLoc(); + Filename = PresumedLoc.getFilename(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (IncludeLoc.isValid()) { if (llvm::timeTraceProfilerEnabled()) { - OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getFileID(Loc)); + OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID); ProfilerStack.push_back(llvm::timeTraceAsyncProfilerBegin( - "Source", FE ? FE->getName() : StringRef(""))); + "Source", FE ? FE->getName() : Filename)); } - IncludeStack.push_back(IncludeLoc); + // TO_UPSTREAM(BoundsSafety) + PushInclude(IncludeLoc, Loc); S->DiagnoseNonDefaultPragmaAlignPack( Sema::PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude, IncludeLoc); @@ -196,13 +240,67 @@ class SemaPPCallbacks : public PPCallbacks { S->DiagnoseNonDefaultPragmaAlignPack( Sema::PragmaAlignPackDiagnoseKind::ChangedStateAtExit, - IncludeStack.pop_back_val()); + PopInclude()); } break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case SystemHeaderPragma: + IncludeStack.back().FAttr = SystemHeaderAttributes(); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ default: break; } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + S->CurPointerAbi = + IncludeStack.empty() ? RootFAttr : IncludeStack.back().FAttr; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) + override { + if (!S) + return; + BoundsSafetyPointerAttributes FAttr; + auto GNU = AttributeCommonInfo::AS_GNU; + for (const IdentifierInfo *II : Spec) { + auto Kind = AttributeCommonInfo::getParsedKind(II, /*ScopeName=*/nullptr, + GNU); + auto BoundsAttrPrev = FAttr.getBoundsAttr(); + switch (Kind) { + case AttributeCommonInfo::AT_PtrBidiIndexable: + FAttr.setBidiIndexable(); + break; + case AttributeCommonInfo::AT_PtrIndexable: + FAttr.setIndexable(); + break; + case AttributeCommonInfo::AT_PtrUnsafeIndexable: + FAttr.setUnsafeIndexable(); + break; + case AttributeCommonInfo::AT_PtrSingle: + FAttr.setSingle(); + break; + default: + S->Diag(Loc, diag::err_bounds_safety_abi_ptr_attr_invalid) << II; + break; + } + if (BoundsAttrPrev && BoundsAttrPrev != FAttr.getBoundsAttr()) { + S->Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* bound */ 0; + continue; + } + } + (IncludeStack.empty() ? RootFAttr : IncludeStack.back().FAttr) = FAttr; + S->CurPointerAbi = FAttr; } + + BoundsSafetyPointerAttributes SystemHeaderAttributes() const { + return BoundsSafetyPointerAttributes(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ }; } // end namespace sema @@ -297,6 +395,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, PP.addPPCallbacks(std::move(Callbacks)); SemaPPCallbackHandler->set(*this); + /* TO_UPSTREAM(BoundsSafety) ON*/ + CurPointerAbi.setSingle(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod()); } @@ -691,10 +793,11 @@ void Sema::diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E) { /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. /// The result is of the given category. -ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, - CastKind Kind, ExprValueKind VK, - const CXXCastPath *BasePath, - CheckedConversionKind CCK) { +ExprResult +Sema::ImpCastExprToType(Expr *E, QualType Ty, CastKind Kind, ExprValueKind VK, + const CXXCastPath *BasePath, CheckedConversionKind CCK, + //TO_UPSTREAM(BoundsSafety) + bool DiagnoseBoundsSafetyIncompleteArrayPromotion) { #ifndef NDEBUG if (VK == VK_PRValue && !E->isPRValue()) { switch (Kind) { @@ -727,7 +830,10 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); - if (ExprTy == TypeTy) + // BoundsSafety: Even if source and destination have the same canonical type, + // we still insert ImplicitCastExpr for types with different -fbounds-safety pointer + // attributes. + if (ExprTy == TypeTy && Kind != CK_BoundsSafetyPointerCast) return E; if (Kind == CK_ArrayToPointerDecay) { @@ -762,8 +868,94 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: a reference to array decays into a bound pointer. + // FIXME: This should also be supported without '-fbounds-safety' when destination + // of this expression is annotated with '__bidi_indexable' or '__indexable' + // (rdar://70018442). + // XXX: We don't promote builtin va_list to bound type for now to preserve the + // compatibility with the builtin function prototype + auto PreservingAttributes = [=](QualType QT, auto Callback) -> QualType { + SmallVector AttrKinds; + while (auto AT = QT->getAs()) { + AttrKinds.push_back(AT->getAttrKind()); + QT = AT->getModifiedType(); + } + + QT = Callback(QT); + + QualType EquivQT = QT; + for (auto I = AttrKinds.rbegin(), E = AttrKinds.rend(); I != E; ++I) + QT = Context.getAttributedType(*I, QT, EquivQT); + return QT; + }; + + if (getLangOpts().BoundsSafety) { + switch (Kind) { + case CK_ArrayToPointerDecay: { + if (!Context.hasSameUnqualifiedType( + ExprTy, Context.getBuiltinVaListType())) { + if (const auto *VTT = E->getType()->getAs()) { + Ty = PreservingAttributes(Ty, [=](QualType QT) { + QualType PT = Context.getPointerType( + QT->getPointeeType(), BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(PT, VTT->getTerminatorExpr()); + }); + break; + } + if (E->getType()->isIncompleteArrayType() && + !E->getType()->isCountAttributedType() && + DiagnoseBoundsSafetyIncompleteArrayPromotion) { + Diag(E->getExprLoc(), + diag::warn_bounds_safety_promoting_incomplete_array_without_count); + } + Ty = PreservingAttributes(Ty, [=](QualType QT) { + auto PT = QT->getAs(); + BoundsSafetyPointerAttributes FA; + FA.setBidiIndexable(); + return Context.getPointerType(PT->getPointeeType(), FA); + }); + } + break; + } + + case CK_FunctionToPointerDecay: { + Ty = PreservingAttributes(Ty, [=](QualType QT) { + BoundsSafetyPointerAttributes FA; + FA.setSingle(); + auto PT = Ty->getAs(); + return Context.getPointerType(PT->getPointeeType(), FA); + }); + break; + } + + default: + break; + } + } else if (getLangOpts().DebuggerSupport && + (Kind == CK_BitCast || Kind == CK_NoOp) && + E->getType()->isPointerType() && Ty->isPointerType()) { + auto SrcPTy = E->getType()->getAs(); + auto DstPTy = Ty->getAs(); + + if (SrcPTy->getPointerAttributes() != DstPTy->getPointerAttributes()) { + if (Kind == CK_BitCast) { + QualType BridgeDstTy = PreservingAttributes(Ty, [&](QualType QT) { + return Context.getPointerType(SrcPTy->getPointeeType(), + DstPTy->getPointerAttributes()); + }); + E = ImplicitCastExpr::Create(Context, BridgeDstTy, CK_BoundsSafetyPointerCast, E, BasePath, VK, + CurFPFeatureOverrides()); + } else { + Kind = CK_BoundsSafetyPointerCast; + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (ImplicitCastExpr *ImpCast = dyn_cast(E)) { - if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) { + if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty()) && + Kind != CK_BoundsSafetyPointerCast) { ImpCast->setType(Ty); ImpCast->setValueKind(VK); return E; @@ -1384,6 +1576,14 @@ void Sema::ActOnEndOfTranslationUnit() { diag::err_tentative_def_incomplete_type)) VD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Now perform VarDecl checks for tentative definitions. Non-tentative + // definitions were already handled earlier in + // `Sema::FinalizeDeclaratorGroup()`. + if (!BoundsSafetyCheckVarDecl(VD, /*CheckTentativeDefinitions=*/true)) + VD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // No initialization is performed for a tentative definition. CheckCompleteVariableDeclaration(VD); @@ -2768,3 +2968,18 @@ bool Sema::isDeclaratorFunctionLike(Declarator &D) { }); return Result; } + +/*TO_UPSTREAM(BoundsSafety) ON*/ +bool Sema::BoundsSafetyFixItWasEmittedFor(const DeclaratorDecl *DD, bool Set) { + assert(DD); + assert(isa(DD) || isa(DD)); + if (Set) { + // Record the Decl + BoundsSafetyDeclsWithFixIts.insert(DD); + return true; + } + + // Retrieve whether a FixIt was emitted for this VarDecl. + return BoundsSafetyDeclsWithFixIts.contains(DD); +} + /*TO_UPSTREAM(BoundsSafety) OFF*/ diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 9f09e55d0a579..913baed40a2c1 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -409,6 +409,56 @@ static void ProcessAPINotes(Sema &S, Decl *D, Metadata); } +/* TO_UPSTREAM(BoundsSafety) ON */ +static void applyBoundsSafety(Sema &S, ValueDecl *D, + const api_notes::BoundsSafetyInfo &Info, + VersionedInfoMetadata Metadata) { + using BoundsSafetyKind = api_notes::BoundsSafetyInfo::BoundsSafetyKind; + if (Metadata.IsActive && !Info.ExternalBounds.empty() && + S.ParseBoundsAttributeArgFromStringCallback) { + Decl *ScopeDecl = D; + if (auto ParmDecl = dyn_cast(ScopeDecl)) { + ScopeDecl = dyn_cast(ParmDecl->getDeclContext()); + } + auto ParsedExpr = S.ParseBoundsAttributeArgFromStringCallback( + Info.ExternalBounds, "", ScopeDecl, D->getLocation()); + if (ParsedExpr.isInvalid()) + return; + + std::string AttrName; + AttributeCommonInfo::Kind Kind; + assert(*Info.getKind() >= BoundsSafetyKind::CountedBy); + assert(*Info.getKind() <= BoundsSafetyKind::EndedBy); + switch (*Info.getKind()) { + case BoundsSafetyKind::CountedBy: + AttrName = "__counted_by"; + Kind = AttributeCommonInfo::AT_CountedBy; + break; + case BoundsSafetyKind::CountedByOrNull: + AttrName = "__counted_by_or_null"; + Kind = AttributeCommonInfo::AT_CountedByOrNull; + break; + case BoundsSafetyKind::SizedBy: + AttrName = "__sized_by"; + Kind = AttributeCommonInfo::AT_SizedBy; + break; + case BoundsSafetyKind::SizedByOrNull: + AttrName = "__sized_by_or_null"; + Kind = AttributeCommonInfo::AT_SizedByOrNull; + break; + case BoundsSafetyKind::EndedBy: + AttrName = "__ended_by"; + Kind = AttributeCommonInfo::AT_PtrEndedBy; + break; + } + + S.applyPtrCountedByEndedByAttr( + D, *Info.getLevel(), Kind, ParsedExpr.get(), D->getLocation(), + D->getSourceRange(), AttrName, /* originates in API notes */ true); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Process API notes for a parameter. static void ProcessAPINotes(Sema &S, ParmVarDecl *D, const api_notes::ParamInfo &Info, @@ -426,6 +476,11 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D, LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo()); }); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Info.BoundsSafety.has_value()) + applyBoundsSafety(S, D, *Info.BoundsSafety, Metadata); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Retain count convention handleAPINotedRetainCountConvention(S, D, Metadata, Info.getRetainCountConvention()); diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index d8dd4fe16e3af..b0f0fec850256 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -774,8 +774,8 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, if (PtrArgNum >= 0) { // Check that pointer arguments have the specified type. Expr *Arg = TheCall->getArg(PtrArgNum); - if (ImplicitCastExpr *ICE = dyn_cast(Arg)) - Arg = ICE->getSubExpr(); + // TO_UPSTREAM(BoundsSafety) + Arg = Arg->IgnoreImpCasts(); ExprResult RHS = SemaRef.DefaultFunctionArrayLvalueConversion(Arg); QualType RHSTy = RHS.get()->getType(); @@ -790,7 +790,11 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, EltTy = EltTy.withConst(); QualType LHSTy = getASTContext().getPointerType(EltTy); Sema::AssignConvertType ConvTy; - ConvTy = SemaRef.CheckSingleAssignmentConstraints(LHSTy, RHS); + /* TO_UPSTREAM(BoundsSafety) ON*/ + ConvTy = SemaRef.CheckSingleAssignmentConstraints( + LHSTy, RHS, /*Diagnose=*/true, /*DiagnoseCFA=*/false, + /*ConvertRHS=*/true); + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (RHS.isInvalid()) return true; if (SemaRef.DiagnoseAssignmentResult(ConvTy, Arg->getBeginLoc(), LHSTy, diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index 290c820938898..1684c12f09e11 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -11,11 +11,17 @@ /// (e.g. `counted_by`) /// //===----------------------------------------------------------------------===// +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/Initialization.h" #include "clang/Sema/Sema.h" namespace clang { -static CountAttributedType::DynamicCountPointerKind +// In upstream the return type is `CountAttributedType::DynamicCountPointerKind` +/* TO_UPSTREAM(BoundsSafety) ON*/ +CountAttributedType::BoundsAttrKind +/* TO_UPSTREAM(BoundsSafety) OFF*/ getCountAttrKind(bool CountInBytes, bool OrNull) { if (CountInBytes) return OrNull ? CountAttributedType::SizedByOrNull @@ -24,14 +30,60 @@ getCountAttrKind(bool CountInBytes, bool OrNull) { : CountAttributedType::CountedBy; } -static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { +static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD, + // TO_UPSTREAM(BoundsSafety) + Sema &S) { const auto *RD = FD->getParent(); // An unnamed struct is anonymous struct only if it's not instantiated. // However, the struct may not be fully processed yet to determine // whether it's anonymous or not. In that case, this function treats it as // an anonymous struct and tries to find a named parent. - while (RD && (RD->isAnonymousStructOrUnion() || - (!RD->isCompleteDefinition() && RD->getName().empty()))) { + + /* TO_UPSTREAM(BoundsSafety) ON*/ + const auto *ParentOfDeclWithAttr = FD->getParent(); + auto ShouldGetParent = [&]() -> bool { + if (!S.getLangOpts().ExperimentalLateParseAttributes || + RD != ParentOfDeclWithAttr) { + // This is the condition in upstream + return (RD->isAnonymousStructOrUnion() || + (!RD->isCompleteDefinition() && RD->getName().empty())); + } + // In `Parser::ParseStructUnionBody` we have an Apple Internal change + // to call `Actions.ActOnFields` **before** late parsed attributes are + // semantically checked. In upstream `Action.ActOnFields` is called + // afterwards. The effect of this is observable in this function because + // `RD->isCompleteDefinition()` will return true for the struct we are + // processing the attributes on with the Apple Internal change and false + // in upstream. + // + // E.g.: + // + // struct on_pointer_anon_buf { + // int count; + // struct { + // struct size_known *buf __counted_by(count); + // }; // <<-- Processing late parsed attrs of this struct + // }; + // + // For this particular example what's also counter-intuitive is that + // `RD->isAnonymousStructOrUnion()` returns false for the anonymous + // struct, that's because the `;` after the struct hasn't been processed + // yet so it hasn't been marked as anonymous yet. + + // HACK: + // To make lit tests work we don't test `RD->isCompleteDefinition()` + // when it's the RecordDecl that contains the FieldDecl with a `counted_by` + // like attribute (that we are in the middle of checking). Once we've gone + // beyond that RecordDecl we traverse just like upstream clang does. + // + // TODO: Remove this hack once we upstream the `Actions.ActOnFields` + // change (rdar://133402603). + assert(RD == ParentOfDeclWithAttr); + return RD->isAnonymousStructOrUnion() || RD->getName().empty(); + }; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + while (RD && ShouldGetParent()) { const auto *Parent = dyn_cast(RD->getParent()); if (!Parent) break; @@ -173,8 +225,11 @@ bool Sema::CheckCountedByAttrOnField( // Whether CountRD is an anonymous struct is not determined at this // point. Thus, an additional diagnostic in case it's not anonymous struct // is done later in `Parser::ParseStructDeclaration`. - auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); - auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Upstream doesn't pass `*this`. + auto *RD = GetEnclosingNamedOrTopAnonRecord(FD, *this); + auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD, *this); + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (RD != CountRD) { Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct) @@ -190,4 +245,573 @@ bool Sema::CheckCountedByAttrOnField( return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static SourceRange SourceRangeFor(const CountAttributedType *CATy, Sema &S) { + // Note: This implementation relies on `CountAttributedType` being unique. + // E.g.: + // + // struct Foo { + // int count; + // char* __counted_by(count) buffer; + // char* __counted_by(count) buffer2; + // }; + // + // The types of `buffer` and `buffer2` are unique. The types being + // unique means the SourceLocation of the `counted_by` expression can be used + // to find where the attribute was written. + + auto Fallback = CATy->getCountExpr()->getSourceRange(); + auto CountExprBegin = CATy->getCountExpr()->getBeginLoc(); + + // FIXME: We currently don't support the count expression being a macro + // itself. E.g.: + // + // #define ZERO 0 + // int* __counted_by(ZERO) x; + // + if (S.SourceMgr.isMacroBodyExpansion(CountExprBegin)) + return Fallback; + + auto FetchIdentifierTokenFromOffset = + [&](ssize_t Offset) -> std::optional { + SourceLocation OffsetLoc = CountExprBegin.getLocWithOffset(Offset); + Token Result; + if (Lexer::getRawToken(OffsetLoc, Result, S.SourceMgr, S.getLangOpts())) + return std::optional(); // Failed + + if (!Result.isAnyIdentifier()) + return std::optional(); // Failed + + return Result; // Success + }; + + auto CountExprEnd = CATy->getCountExpr()->getEndLoc(); + auto FindRParenTokenAfter = [&]() -> std::optional { + auto CountExprEndSpelling = S.SourceMgr.getSpellingLoc(CountExprEnd); + auto MaybeRParenTok = Lexer::findNextToken( + CountExprEndSpelling, S.getSourceManager(), S.getLangOpts()); + + if (!MaybeRParenTok.has_value()) + return std::nullopt; + + if (!MaybeRParenTok->is(tok::r_paren)) + return std::nullopt; + + return *MaybeRParenTok; + }; + + // Step back two characters to point at the last character of the attribute + // text. + // + // __counted_by(count) + // ^ ^ + // | | + // --- + auto MaybeLastAttrCharToken = FetchIdentifierTokenFromOffset(-2); + if (!MaybeLastAttrCharToken) + return Fallback; + + auto LastAttrCharToken = MaybeLastAttrCharToken.value(); + + if (LastAttrCharToken.getLength() > 1) { + // Special case: When the character is part of a macro the Token we get + // is the whole macro name (e.g. `__counted_by`). + if (LastAttrCharToken.getRawIdentifier() != + CATy->GetAttributeName(/*WithMacroPrefix=*/true)) + return Fallback; + + // Found the beginning of the `__counted_by` macro + SourceLocation Begin = LastAttrCharToken.getLocation(); + // Now try to find the closing `)` of the macro. + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(Begin, MaybeRParenTok->getLocation()); + } + + assert(LastAttrCharToken.getLength() == 1); + // The Token we got back is just the last character of the identifier. + // This means a macro is not being used and instead the attribute is being + // used directly. We need to find the beginning of the identifier. We support + // two cases: + // + // * Non-affixed version. E.g: `counted_by` + // * Affixed version. E.g.: `__counted_by__` + + // Try non-affixed version. E.g.: + // + // __attribute__((counted_by(count))) + // ^ ^ + // | | + // ------------ + + // +1 is for `(` + const ssize_t NonAffixedSkipCount = + CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1; + auto MaybeNonAffixedBeginToken = + FetchIdentifierTokenFromOffset(-NonAffixedSkipCount); + if (!MaybeNonAffixedBeginToken) + return Fallback; + + auto NonAffixedBeginToken = MaybeNonAffixedBeginToken.value(); + if (NonAffixedBeginToken.getRawIdentifier() == + CATy->GetAttributeName(/*WithMacroPrefix=*/false)) { + // Found the beginning of the `counted_by`-like attribute + auto SL = NonAffixedBeginToken.getLocation(); + + // Now try to find the closing `)` of the attribute + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(SL, MaybeRParenTok->getLocation()); + } + + // Try affixed version. E.g.: + // + // __attribute__((__counted_by__(count))) + // ^ ^ + // | | + // ---------------- + std::string AffixedTokenStr = + (llvm::Twine("__") + CATy->GetAttributeName(/*WithMacroPrefix=*/false) + + llvm::Twine("__")) + .str(); + // +1 is for `(` + // +4 is for the 4 `_` characters + const ssize_t AffixedSkipCount = + CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1 + 4; + auto MaybeAffixedBeginToken = + FetchIdentifierTokenFromOffset(-AffixedSkipCount); + if (!MaybeAffixedBeginToken) + return Fallback; + + auto AffixedBeginToken = MaybeAffixedBeginToken.value(); + if (AffixedBeginToken.getRawIdentifier() != AffixedTokenStr) + return Fallback; + + // Found the beginning of the `__counted_by__`-like like attribute. + auto SL = AffixedBeginToken.getLocation(); + // Now try to find the closing `)` of the attribute + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(SL, MaybeRParenTok->getLocation()); +} + +static void EmitIncompleteCountedByPointeeNotes(Sema &S, + const CountAttributedType *CATy, + NamedDecl *IncompleteTyDecl, + bool NoteAttrLocation = true) { + assert(IncompleteTyDecl == nullptr || isa(IncompleteTyDecl)); + + if (NoteAttrLocation) { + // Note where the attribute is declared + auto AttrSrcRange = SourceRangeFor(CATy, S); + S.Diag(AttrSrcRange.getBegin(), diag::note_named_attribute) + << CATy->GetAttributeName(/*WithMacroPrefix=*/true) << AttrSrcRange; + } + + if (!IncompleteTyDecl) + return; + + // If there's an associated forward declaration display it to emphasize + // why the type is incomplete (all we have is a forward declaration). + + // Note the `IncompleteTyDecl` type is the underlying type which might not + // be the same as `CATy->getPointeeType()` which could be a typedef. + // + // The diagnostic printed will be at the location of the underlying type but + // the diagnostic text will print the type of `CATy->getPointeeType()` which + // could be a typedef name rather than the underlying type. This is ok + // though because the diagnostic will print the underlying type name too. + // E.g: + // + // `forward declaration of 'Incomplete_Struct_t' + // (aka 'struct IncompleteStructTy')` + // + // If this ends up being confusing we could emit a second diagnostic (one + // explaining where the typedef is) but that seems overly verbose. + + S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration) + << CATy->getPointeeType(); +} + +static bool +HasCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND, + const CountAttributedType **CATyOut, + QualType *PointeeTyOut) { + auto *CATy = Ty->getAs(); + if (!CATy) + return false; + + // Incomplete pointee type is only a problem for + // counted_by/counted_by_or_null + if (CATy->isCountInBytes()) + return false; + + auto PointeeTy = CATy->getPointeeType(); + if (PointeeTy.isNull()) + return false; // Reachable? + + if (!PointeeTy->isIncompleteType(ND)) + return false; + + if (CATyOut) + *CATyOut = CATy; + if (PointeeTyOut) + *PointeeTyOut = PointeeTy; + return true; +} + +bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, std::function ComputeAssignee) { + NamedDecl *IncompleteTyDecl = nullptr; + const CountAttributedType *CATy = nullptr; + QualType PointeeTy; + if (!HasCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl, &CATy, + &PointeeTy)) + return true; + assert(CATy && !CATy->isCountInBytes() && !PointeeTy.isNull()); + + // It's not expected that the diagnostic be emitted in these cases. + // It's not necessarily a problem but we should catch when this starts + // to happen. + assert(Action != Sema::AA_Converting && Action != Sema::AA_Sending && + Action != Sema::AA_Casting && Action != Sema::AA_Passing_CFAudited); + + // By having users provide a function we only pay the cost of allocation the + // string and computing when a diagnostic is emitted. + std::string Assignee = ComputeAssignee ? ComputeAssignee() : ""; + { + auto D = + Diag(Loc, diag::err_bounds_safety_counted_by_on_incomplete_type_on_assign) + << /*0*/ (int)Action << /*1*/ Assignee << /*2*/ (Assignee.size() > 0) + << /*3*/ isa(RHSExpr) << /*4*/ LHSTy + << /*5*/ CATy->GetAttributeName(/*WithMacroPrefix=*/true) + << /*6*/ PointeeTy << /*7*/ CATy->isOrNull(); + + if (RHSExpr->getSourceRange().isValid()) + D << RHSExpr->getSourceRange(); + } + + EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl); + return false; // check failed +} + +bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, std::function ComputeAssignee) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + return BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + LHSTy, RHSExpr, Action, Loc, ComputeAssignee); +} + +bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity, + const InitializationKind &Kind, + Sema::AssignmentAction Action, + QualType LHSType, Expr *RHSExpr) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + bool ChecksPassed = true; + auto SL = Kind.getLocation(); + + // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here + // because we need conditionalize what is checked. + + // counted_by/counted_by_or_null on incomplete pointee type check. + // + // We skip Variable initializers because we should have already complained + // about those variables in `Sema::BoundsSafetyCheckVarDecl()`. + if (Action == Sema::AA_Initializing && + Entity.getKind() != InitializedEntity::EK_Variable) { + + if (!BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + LHSType, RHSExpr, Action, SL, [&Entity]() -> std::string { + if (const auto *VD = + dyn_cast_or_null(Entity.getDecl())) { + return VD->getQualifiedNameAsString(); + } + return ""; + })) { + ChecksPassed = false; + + // It's not necessarily bad if this assert fails but we should catch + // if this happens. + assert(Entity.getKind() == InitializedEntity::EK_Member); + } + } + + return ChecksPassed; +} + +static bool BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy(Sema &S, + Expr *E) { + QualType T = E->getType(); + assert(T->isPointerType()); + + // Generate a string for the diagnostic that describes the "use". + // The string is specialized for direct calls to produce a better + // diagnostic. + StringRef DirectCallFn; + std::string UseStr; + if (const auto *CE = dyn_cast(E->IgnoreParens())) { + if (const auto *FD = CE->getDirectCallee()) { + DirectCallFn = FD->getName(); + } + } + int SelectExprKind = DirectCallFn.size() > 0 ? 1 : 0; + if (SelectExprKind) { + UseStr = DirectCallFn; + } else { + llvm::raw_string_ostream SS(UseStr); + E->printPretty(SS, nullptr, PrintingPolicy(S.getPrintingPolicy())); + } + assert(UseStr.size() > 0); + + const CountAttributedType *CATy = nullptr; + QualType PointeeTy; + NamedDecl *IncompleteTyDecl = nullptr; + if (!HasCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl, &CATy, + &PointeeTy)) + return true; + assert(CATy && !CATy->isCountInBytes() && !PointeeTy.isNull()); + + S.Diag(E->getBeginLoc(), + diag::err_bounds_safety_counted_by_on_incomplete_type_on_use) + << /*0*/ SelectExprKind << /*1*/ UseStr << /*2*/ T << /*3*/ PointeeTy + << /*4*/ CATy->GetAttributeName(/*WithMacroPrefix=*/true) + << /*5*/ CATy->isOrNull() << E->getSourceRange(); + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl); + return false; +} + +bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(Expr *E) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + QualType T = E->getType(); + if (!T->isPointerType()) + return true; + + return BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy(*this, E); +} + +bool Sema::BoundsSafetyCheckResolvedCall(FunctionDecl *FDecl, CallExpr *Call, + const FunctionProtoType *ProtoType) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + assert(Call); + if (Call->containsErrors()) + return false; + + // Report incomplete pointee types on `__counted_by(__or_null)` pointers. + bool ChecksPassed = true; + + // Check the return of the call. The call is treated as a "use" of + // the return type. + if (!BoundsSafetyCheckUseOfCountAttrPtr(Call)) + ChecksPassed = false; + + // Check parameters + if (!FDecl && !ProtoType) + return ChecksPassed; // Can't check any further so return early + + unsigned MinNumArgs = + std::min(Call->getNumArgs(), + FDecl ? FDecl->getNumParams() : ProtoType->getNumParams()); + + for (size_t ArgIdx = 0; ArgIdx < MinNumArgs; ++ArgIdx) { + Expr *CallArg = Call->getArg(ArgIdx); // FIXME: IgnoreImpCast()? + StringRef ParamName; + QualType ParamTy; + if (FDecl) { + // Direct call + auto *ParamVarDecl = FDecl->getParamDecl(ArgIdx); + ParamTy = ParamVarDecl->getType(); + ParamName = ParamVarDecl->getName(); + } else { + // Indirect call. The parameter name isn't known + ParamTy = ProtoType->getParamType(ArgIdx); + } + + // Assigning to the parameter type is treated as a "use" of the type. + if (!BoundsSafetyCheckAssignmentToCountAttrPtr( + ParamTy, CallArg, Sema::AA_Passing, CallArg->getBeginLoc(), + [&ParamName]() { return ParamName.str(); })) + ChecksPassed = false; + } + return ChecksPassed; +} + +static bool BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + Sema &S, QualType Ty, const ParmVarDecl *ParamDecl) { + const CountAttributedType *CATy = nullptr; + QualType PointeeTy; + NamedDecl *IncompleteTyDecl = nullptr; + if (!HasCountedByAttrOnIncompletePointee(Ty, &IncompleteTyDecl, &CATy, + &PointeeTy)) + return true; + + // Emit Diagnostic + StringRef ParamName; + if (ParamDecl) + ParamName = ParamDecl->getName(); + + auto SR = SourceRangeFor(CATy, S); + S.Diag(SR.getBegin(), + diag::err_bounds_safety_counted_by_on_incomplete_type_on_func_def) + << /*0*/ CATy->GetAttributeName(/*WithMacroPrefix*/ true) + << /*1*/ (ParamDecl ? 1 : 0) << /*2*/ (ParamName.size() > 0) + << /*3*/ ParamName << /*4*/ Ty << /*5*/ PointeeTy + << /*6*/ CATy->isOrNull() << SR; + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl, + /*NoteAttrLocation=*/false); + return false; +} + +bool Sema::BoundsSafetyCheckParamForFunctionDef(const ParmVarDecl *PVD) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + return BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + *this, PVD->getType(), PVD); +} + +bool Sema::BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + return BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + *this, FD->getReturnType(), nullptr); +} + +static bool +BoundsSafetyCheckVarDeclCountAttrPtrWithIncompletePointeeTy(Sema &S, + const VarDecl *VD) { + const CountAttributedType *CATy = nullptr; + QualType PointeeTy; + NamedDecl *IncompleteTyDecl = nullptr; + if (!HasCountedByAttrOnIncompletePointee(VD->getType(), &IncompleteTyDecl, + &CATy, &PointeeTy)) + return true; + + SourceRange SR = SourceRangeFor(CATy, S); + S.Diag(SR.getBegin(), + diag::err_bounds_safety_counted_by_on_incomplete_type_on_var_decl) + << /*0*/ CATy->GetAttributeName(/*WithMacroPrefix=*/true) + << /*1*/ (VD->isThisDeclarationADefinition() == + VarDecl::TentativeDefinition) + << /*2*/ VD->getName() << /*3*/ VD->getType() << /*4*/ PointeeTy + << /*5*/ CATy->isOrNull() << SR; + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl, + /*NoteAttrLocation=*/false); + return false; +} + +bool Sema::BoundsSafetyCheckVarDecl(const VarDecl *VD, + bool CheckTentativeDefinitions) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + switch (VD->isThisDeclarationADefinition()) { + case VarDecl::DeclarationOnly: + // Using `__counted_by` on a pointer type with an incomplete pointee + // isn't considered an error for declarations. + return true; + case VarDecl::TentativeDefinition: + // A tentative definition may become an actual definition but this isn't + // known until the end of the translation unit. + // See `Sema::ActOnEndOfTranslationUnit()` + if (!CheckTentativeDefinitions) + return true; + LLVM_FALLTHROUGH; + case VarDecl::Definition: + // Using `__counted_by` on a pointer type with an incomplete pointee + // is considered an error for **definitions** so carry-on with checking. + break; + } + + return BoundsSafetyCheckVarDeclCountAttrPtrWithIncompletePointeeTy(*this, VD); +} + +bool Sema::BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + const CountAttributedType *CATTy, Expr *Operand, + std::variant OpInfo) { + + bool IsUnaryOp = std::holds_alternative(OpInfo); + int SelectOp = 0; + unsigned DiagID = 0; + if (IsUnaryOp) { + SelectOp = std::get(OpInfo); + DiagID = + diag::warn_bounds_safety_count_attr_pointer_unary_arithmetic_constant_count; + } else { + // Binary operator + DiagID = + diag::warn_bounds_safety_count_attr_pointer_binary_assign_constant_count; + switch (std::get(OpInfo)) { + case BO_AddAssign: // += + SelectOp = 1; + break; + case BO_SubAssign: // -= + SelectOp = 0; + break; + default: + // We shouldn't go down this path. Other operations on a + // CountAttributedType pointers have the pointer promoted to a + // __bidi_indexable first rather than keeping the CountAttributedType + // type. + llvm_unreachable("Unexpected BinaryOperatorKind"); + return true; + } + } + + Expr::EvalResult Result; + if (CATTy->getCountExpr()->EvaluateAsInt(Result, getASTContext())) { + // Count is constant + Diag(Operand->getExprLoc(), DiagID) << + /*0*/ SelectOp << + /*1*/ CATTy->GetAttributeName(/*WithMacroPrefix=*/true) << + /*2*/ (CATTy->isCountInBytes() ? 1 : 0) << + /*3*/ 0 /* integer constant count*/ << + /*4*/ Result.Val.getAsString(getASTContext(), + CATTy->getCountExpr()->getType()); + Diag(CATTy->getCountExpr()->getExprLoc(), diag::note_named_attribute) + << CATTy->GetAttributeName(/*WithMacroPrefix=*/true); + return false; + } + if (const auto *DRE = + dyn_cast(CATTy->getCountExpr()->IgnoreParenCasts())) { + const auto *VD = DRE->getDecl(); + if (VD->getType().isConstQualified()) { + // Count expression refers to a single decl that is `const` qualified + // which means it is effectively constant. + + Diag(Operand->getExprLoc(), DiagID) << + /*0*/ SelectOp << + /*1*/ CATTy->GetAttributeName(/*WithMacroPrefix=*/true) << + /*2*/ (CATTy->isCountInBytes() ? 1 : 0) << + /*3*/ 1 /* const qualified declref*/ << + /*4*/ VD; + Diag(CATTy->getCountExpr()->getExprLoc(), diag::note_named_attribute) + << CATTy->GetAttributeName(/*WithMacroPrefix=*/true); + return false; + } + } + + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + } // namespace clang diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index eef2393c41b86..bdc93235c0dbf 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -167,6 +167,194 @@ namespace { SrcExpr = src; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + void checkBoundsSafetyConversion(Sema &Self) { + if (SrcExpr.isInvalid()) + return; + + QualType SrcType = SrcExpr.get()->getType(); + + auto const *DestPTy = DestType->getAs(); + auto const *SrcPTy = SrcType->getAs(); + + bool SrcIsNull = false; + bool IsDstNullTerm = false; + bool IsSrcNullTerm = false; + + if (DestPTy && Self.getLangOpts().BoundsSafety) { + // Diagnose int to ptr cast. + // Without -fbounds-safety, int to wide ptr works as unsafe hatch is + // inserted. + Expr::EvalResult R; + SrcIsNull = SrcExpr.get()->isNullPointerConstant(Self.Context, + Expr::NPC_ValueDependentIsNotNull); + if (DestPTy->isSafePointer() && !SrcPTy && + !DestPTy->getPointeeType().isVolatileQualified() && + !SrcIsNull) { + Self.Diag(OpRange.getBegin(), diag::err_bounds_safety_non_to_pointer); + SrcExpr = ExprError(); + return; + } + } + if (!DestPTy || !SrcPTy) + return; + + /// BoundsSafety: Such case should be handled as BoundsSafetyPointerCast. + /// \code + /// int *__bidi_indexable p1; + /// int *__indexable p2 = (int *__indexable)p1; + /// \endcode + if (Self.getLangOpts().BoundsSafety) { + unsigned DiagKind = 0; + bool isInvalid = false; + // The type error may be nested, so any pointer can result in VT errors + Sema::AssignConvertType ConvertTy = + Self.CheckValueTerminatedAssignmentConstraints(DestType, + SrcExpr.get()); + switch (ConvertTy) { + default: + assert(ConvertTy == Sema::Compatible); + break; + case Sema::IncompatibleStringLiteralToValueTerminatedPointer: + DiagKind = + diag::err_bounds_safety_incompatible_string_literal_to_terminated_by; + isInvalid = true; + break; + case Sema::IncompatibleValueTerminatedTerminators: + DiagKind = diag::err_bounds_safety_incompatible_terminated_by_terminators; + isInvalid = true; + break; + case Sema::IncompatibleValueTerminatedToNonValueTerminatedPointer: { + const auto *SrcPointerType = SrcType->getAs(); + IsSrcNullTerm = + SrcPointerType->getTerminatorValue(Self.Context).isZero(); + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by; + isInvalid = true; + break; + } + case Sema::IncompatibleNonValueTerminatedToValueTerminatedPointer: { + const auto *DstPointerType = DestType->getAs(); + IsDstNullTerm = + DstPointerType->getTerminatorValue(Self.Context).isZero(); + if (Self.getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = true; + } + break; + } + case Sema::IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch; + isInvalid = true; + break; + case Sema::IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + if (Self.getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = true; + } + break; + } + if (DiagKind) { + if (DiagKind == + diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by) { + auto FDiag = Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << Sema::AA_Casting + << (IsSrcNullTerm ? /*null_terminated*/ 1 + : /*terminated_by*/ 0); + } else if ( + DiagKind == + diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by || + DiagKind == + diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by) { + auto FDiag = Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << Sema::AA_Casting + << (IsDstNullTerm ? /*null_terminated*/ 1 + : /*terminated_by*/ 0); + } else { + Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << Sema::AA_Casting; + } + + Self.TryFixAssigningNullTerminatedToBidiIndexableExpr(SrcExpr.get(), + DestType); + + Self.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr( + SrcExpr.get(), DestType); + + Self.TryFixAssigningBidiIndexableExprToNullTerminated(SrcExpr.get(), + DestType); + + if (isInvalid) { + SrcExpr = ExprError(); + return; + } + } + + if (DestPTy->isSafePointer() && !SrcPTy->isSafePointer() && + !SrcIsNull) { + if (!SrcPTy->isUnsafeIndexable()) { + // Ensure that the diagnostic says "unsafe indexable" somewhere. + SrcType = Self.Context.getBoundsSafetyPointerType( + SrcType, BoundsSafetyPointerAttributes::unsafeIndexable()); + } + Self.Diag(OpRange.getBegin(), diag::err_bounds_safety_unsafe_to_safe) + << SrcType << DestType << Sema::AA_Casting; + SrcExpr = ExprError(); + return; + } + + if (!SrcIsNull && DestPTy->isPointerTypeWithBounds() && + SrcPTy->isSingle() && !SrcType->isBoundsAttributedType()) { + if (SrcPTy->getPointeeType()->isIncompleteOrSizelessType()) { + Self.Diag(OpRange.getBegin(), + diag::err_bounds_safety_incomplete_single_to_indexable) + << SrcType << DestType << Sema::AA_Casting; + } else { + Self.DiagnoseSingleToWideLosingBounds(DestType, SrcType, SrcExpr.get()); + } + } + } + + ResultType = Self.deduceCastPointerAttributes(ResultType, SrcType); + // Retake the pointer type since DestType has been updated. + const auto *ResultPTy = ResultType->getAs(); + assert(ResultPTy); + // Check if we need BoundsSafetyPointerCast by checking the outermost pointer + // attributes. + if (ResultPTy->getPointerAttributes() != SrcPTy->getPointerAttributes()) { + /// We also want to insert BoundsSafetyPointerCast in the cases like below: + /// \code + /// int *__indexable pi; + /// (char *__bidi_indexable) pi; + /// \endcode + bool SkipNoOp = Kind == CK_NoOp && + Self.Context.hasSameType(ResultPTy->getPointeeType(), + SrcPTy->getPointeeType()); + if (!SkipNoOp) { + QualType Ty = Self.Context.getPointerType( + ResultPTy->getPointeeType(), SrcPTy->getPointerAttributes()); + SrcExpr = Self.ImpCastExprToType(SrcExpr.get(), Ty, Kind, + SrcExpr.get()->getValueKind()); + } + Kind = CK_BoundsSafetyPointerCast; + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void checkQualifiedDestType() { // Destination type may not be qualified with __ptrauth. if (DestType.getPointerAuth()) { @@ -212,6 +400,75 @@ namespace { }; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +QualType +Sema::deduceCastPointerAttributes(QualType ResultType, QualType SrcType) { + if (Context.hasSameBoundsSafetyPointerLayout(ResultType, SrcType)) + return ResultType; + + // We apply the following type attribute inheritance rules. + // If a pointer attribute for the cast type is unspecified, the cast + // type automatically inherits the source pointer attribute. For example, + // in the cases like below, '(int*)val' becomes '(int *__bidi_indexable)val' + // because val is 'int *__bidi_indexable'. + // int *val; // auto bound + // int *val2 = (int*)val; + // If the source is not a pointer, the cast type inherits the default ABI + // visible pointer attribute. + std::function + applyAttrToDestTy = [&](QualType DstTy, QualType SrcTy, + QualType MergePointeeTy, + QualType OrigDstTy) -> QualType { + const auto *DPTy = DstTy->getAs(); + if (DPTy->isUnspecified() || MergePointeeTy != DstTy->getPointeeType()) { + const auto *DVTT = DstTy->getAs(); + + const PointerType *SPTy = nullptr; + const ValueTerminatedType *SVTT = nullptr; + if (!SrcTy.isNull()) { + SPTy = SrcTy->getAs(); + SVTT = SrcTy->getAs(); + } + + if (DVTT) { + // DstTy is __terminated_by(), update only the pointee. + QualType Ty = Context.getPointerType( + MergePointeeTy, BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(Ty, DVTT->getTerminatorExpr()); + } + + if (DPTy->isUnspecified() && SVTT && + Context.hasSameUnqualifiedType(SPTy->getPointeeType(), + MergePointeeTy)) { + // SrcTy is __terminated_by(), update the pointee and inherit the + // __terminated_by(). + assert(!DVTT && SPTy->isSingle()); + QualType Ty = Context.getPointerType( + MergePointeeTy, BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(Ty, SVTT->getTerminatorExpr()); + } + + // Update the pointee and get a new attribute if unspecified. + BoundsSafetyPointerAttributes DstAttr; + if (!DPTy->isUnspecified()) { + DstAttr = DPTy->getPointerAttributes(); + } else if (SPTy) { + DstAttr = SPTy->getPointerAttributes(); + } else { + DstAttr = CurPointerAbi; + } + return Context.getPointerType(MergePointeeTy, DstAttr); + } + + return DstTy; + }; + + // Update Op.ResultType as it is used to create the cast expression. + return Context.mergeBoundsSafetyPointerTypes( + ResultType, SrcType, applyAttrToDestTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr, QualType DestType); @@ -3179,6 +3436,11 @@ void CastOperation::CheckCStyleCast() { if ((Self.Context.getTypeSize(SrcType) > Self.Context.getTypeSize(DestType)) && + /* TO_UPSTREAM(BoundsSafety) ON*/ + (!SrcType->isPointerTypeWithBounds() || + Self.Context.getTypeSize(Self.Context.getIntPtrType()) > + Self.Context.getTypeSize(DestType)) && + /* TO_UPSTREAM(BoundsSafety) OFF*/ !DestType->isBooleanType()) { // C 6.3.2.3p6: Any pointer type may be converted to an integer type. // Except as previously specified, the result is implementation-defined. @@ -3363,6 +3625,10 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, Op.CheckCStyleCast(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + Op.checkBoundsSafetyConversion(*this); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (Op.SrcExpr.isInvalid()) return ExprError(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 93f255a8e8ce5..2807a2e66876e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3045,7 +3045,7 @@ static void CheckNonNullArguments(Sema &S, if (!NonNull->args_size()) { // Easy case: all pointer arguments are nonnull. for (const auto *Arg : Args) - if (S.isValidPointerAttrType(Arg->getType())) + if (isValidPointerAttrType(Arg->getType())) CheckNonNullArgument(S, Arg, CallSiteLoc); return; } @@ -3874,6 +3874,43 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, return ExprError(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) { + const auto *PtrTy = ValType->getAs(); + bool IsDBP = ValType->isBoundsAttributedType(); + if (PtrTy || IsDBP) { + // Check if an AddSub op uses a pointer to __unsafe_indexable pointer. + if (Form == Arithmetic && (IsDBP || PtrTy->isSafePointer())) { + // Non-AddSub ops require a pointer to an integer, they should have been + // handled. + Diag(ExprRange.getBegin(), + diag::err_bounds_safety_atomic_op_arithmetic_needs_unsafe_pointer) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + // Non-arithmetic ops require a pointer to __unsafe_indexable or __single + // pointer. + if (IsDBP || (PtrTy->isSafePointer() && !PtrTy->isSingle())) { + unsigned DiagIndex; + if (PtrTy->isIndexable()) + DiagIndex = 0; + else if (PtrTy->isBidiIndexable()) + DiagIndex = 1; + else if (const auto *DCPTy = ValType->getAs()) + DiagIndex = DCPTy->getKind() + 2; + else if (ValType->isDynamicRangePointerType()) + DiagIndex = 6; + else + llvm_unreachable("Unknown pointer"); + Diag(ExprRange.getBegin(), + diag::err_bounds_safety_atomic_op_unsupported_attribute) + << DiagIndex << Ptr->getSourceRange(); + return ExprError(); + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // All atomic operations have an overload which takes a pointer to a volatile // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself // into the result or the other operands. Similarly atomic_load takes a @@ -6079,6 +6116,9 @@ bool Sema::CheckFormatArguments(ArrayRef Args, } const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts(); + if (auto Forge = dyn_cast(OrigFormatExpr)) { + OrigFormatExpr = Forge->getAddr()->IgnoreParenCasts(); + } // CHECK: format string is not a string literal. // @@ -9183,6 +9223,29 @@ Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType, // here prevent the user from using a PPC MMA type as trailing return type. if (Context.getTargetInfo().getTriple().isPPC64()) PPC().CheckPPCMMAType(RetValExp->getType(), ReturnLoc); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyCheckAssignmentToCountAttrPtr( + lhsType, RetValExp, Sema::AA_Returning, RetValExp->getBeginLoc()); + + // For a count-attributed return type, its dependent count variables can be + // assigned in arbitrary places. Don't try to find the assigned values, just + // assume we don't know them and pass an empty map. + // Pass an empty designator, since it won't be used in diagnostics for + // returning action. + // + // TODO: This diagnostic check should always be performed (rdar://138982703). + // The check is currently guarded because if it's always on several projects + // fail to build (rdar://138798562&138804573&138808353&138855224). + if (getLangOpts().BoundsSafety && + getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) { + DependentValuesMap DependentValues; + CheckDynamicCountSizeForAssignment( + lhsType, RetValExp->IgnoreParenImpCasts(), Sema::AA_Returning, + RetValExp->getBeginLoc(), + /*Designator=*/"", DependentValues); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } void Sema::CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, @@ -11306,7 +11369,8 @@ static void AnalyzeImplicitConversions( SourceLocation CC = Item.CC; QualType T = OrigE->getType(); - Expr *E = OrigE->IgnoreParenImpCasts(); + llvm::SmallPtrSet BoundValues; + Expr *E = OrigE->IgnoreParenImpCasts(BoundValues); // Propagate whether we are in a C++ list initialization expression. // If so, we do not issue warnings for implicit int-float conversion @@ -11428,6 +11492,9 @@ static void AnalyzeImplicitConversions( Expr *ChildExpr = dyn_cast_or_null(SubStmt); if (!ChildExpr) continue; + if (auto *OVE = dyn_cast(ChildExpr)) + if (BoundValues.count(OVE)) + ChildExpr = OVE->getSourceExpr(); if (auto *CSE = dyn_cast(E)) if (ChildExpr == CSE->getOperand()) @@ -12794,6 +12861,13 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef Parameters, HasInvalidParm = true; Diag(Param->getLocation(), diag::err_wasm_table_as_function_parameter); } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!Param->isInvalidDecl() && + !BoundsSafetyCheckParamForFunctionDef(Param)) { + Param->setInvalidDecl(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } return HasInvalidParm; @@ -14135,6 +14209,13 @@ void Sema::DiagnoseMisalignedMembers() { void Sema::DiscardMisalignedMemberAddress(const Type *T, Expr *E) { E = E->IgnoreParens(); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (E->getType()->isSafePointerType()) { + E = E->IgnoreParenImpCasts(); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (!T->isPointerType() && !T->isIntegerType() && !T->isDependentType()) return; if (isa(E) && diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2e72a4d028ec0..aff920f54d0e4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -2312,13 +2314,67 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, // FunctionDecl. if (const FunctionProtoType *FT = dyn_cast(Type)) { SmallVector Params; + // TO_UPSTREAM(BoundsSafety) + SmallVector NewParamTypes; for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { ParmVarDecl *parm = ParmVarDecl::Create( Context, New, SourceLocation(), SourceLocation(), nullptr, FT->getParamType(i), /*TInfo=*/nullptr, SC_None, nullptr); parm->setScopeInfo(0, i); Params.push_back(parm); + // TO_UPSTREAM(BoundsSafety) + NewParamTypes.push_back(FT->getParamType(i)); + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + // We add size annotations for builtin memory manipulation functions + // such that there is a semantic and runtime checks for pointer arguments + // with sizes. + unsigned MemFnKind = New->getMemoryFunctionKind(); + bool NeedReturnSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImemset || + MemFnKind == Builtin::BImemmove; + bool NeedDestSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImempcpy || + MemFnKind == Builtin::BImemset || + MemFnKind == Builtin::BImemmove; + bool NeedSrcSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImempcpy || + MemFnKind == Builtin::BImemmove; + + auto BuildNewDynamicSizeType = [&](QualType Ty) { + auto NewSizeRef = DeclRefExpr::Create(Context, Params[2]->getQualifierLoc(), + SourceLocation(), Params[2], true, SourceLocation(), Params[2]->getType(), VK_LValue, Params[2]); + return BuildCountAttributedType(Ty, NewSizeRef, /*CountInBytes*/true); + }; + if (NeedReturnSize || NeedDestSize || NeedSrcSize) { + assert(FT->getNumParams() >= 3); + if (NeedDestSize) { + QualType NewParmTy = BuildNewDynamicSizeType(Params[0]->getType()); + Params[0]->setType(NewParmTy); + NewParamTypes[0] = Params[0]->getType(); + const auto *CATy = NewParmTy->getAs(); + AttachDependerDeclsAttr(Params[0], CATy, /*Level*/ 0); + } + if (NeedSrcSize) { + QualType NewParmTy = BuildNewDynamicSizeType(Params[1]->getType()); + Params[1]->setType(NewParmTy); + NewParamTypes[1] = Params[1]->getType(); + const auto *CATy = NewParmTy->getAs(); + AttachDependerDeclsAttr(Params[1], CATy, /*Level*/ 0); + } + QualType NewReturnType = FT->getReturnType(); + if (NeedReturnSize) { + NewReturnType = BuildNewDynamicSizeType(NewReturnType); + } + QualType NewFuncType = Context.getFunctionType(NewReturnType, + NewParamTypes, FT->getExtProtoInfo()); + New->setType(NewFuncType); + } } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + New->setParams(Params); } @@ -3532,6 +3588,618 @@ static void adjustDeclContextForDeclaratorDecl(DeclaratorDecl *NewD, FixSemaDC(VD->getDescribedVarTemplate()); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +struct TransposeDynamicBoundsExpr + : public TreeTransform { + using BaseClass = TreeTransform; + + FunctionDecl *NewFD; + bool Success = true; + + static bool Rewrite(Sema &SemaRef, FunctionDecl *NewFD, Expr *E, + std::string &Result) { + bool Invalid; + auto TokRange = CharSourceRange::getTokenRange(E->getSourceRange()); + Result = Lexer::getSourceText(TokRange, SemaRef.SourceMgr, + SemaRef.getLangOpts(), &Invalid); + if (Invalid) + return false; + + TransposeDynamicBoundsExpr Rewriter(SemaRef, NewFD); + Rewriter.TransformExpr(E); + return Rewriter.Success; + } + + TransposeDynamicBoundsExpr(Sema &SemaRef, FunctionDecl *NewFD) + : BaseClass(SemaRef), NewFD(NewFD) {} + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + assert(isa(E->getDecl())); + auto *OldParm = cast(E->getDecl()); + auto OldName = OldParm->getDeclName(); + auto *NewParm = NewFD->getParamDecl(OldParm->getFunctionScopeIndex()); + auto NewName = NewParm->getDeclName(); + // Parameter names have to match because we don't know if the only + // difference between the two prototypes are the pointer attributes. A + // developer can make other changes at the same time, like swapping + // arguments. It's also possible/common that the prototype in the header + // just doesn't have parameter names at all, in which case it's reckless to + // make them up–the header could be included by files that use macros with + // the same name as the parameter names we would like to use. + Success &= OldName == NewName; + return E; + } +}; + +static TypeLoc getPointeeTypeLoc(TypeLoc TL) { + if (TL) { + TL = TL.getAsAdjusted(); + + if (auto DCPTL = TL.getAs()) + return getPointeeTypeLoc(DCPTL.getInnerLoc()); + if (auto DRPTL = TL.getAs()) + return getPointeeTypeLoc(DRPTL.getInnerLoc()); + if (auto PTL = TL.getAs()) + return PTL.getPointeeLoc(); + } + return TypeLoc(); +} + +/// fixBoundsSafetyTypeLocs - emit fixits on diagnostic D to adjust type B/QB to +/// match the bounds indicated on type A/QA. A/QA is not reciprocally adjusted +/// if B/QB has qualifiers; this function needs to be called once for each +/// direction. +static void fixBoundsSafetyTypeLocs(Sema &S, Sema::SemaDiagnosticBuilder &D, + TypeLoc A, TypeLoc B, QualType QA, QualType QB, + FunctionDecl *ADecl, FunctionDecl *BDecl) { + if (ADecl->getNumParams() != BDecl->getNumParams()) + return; + + auto *PA = QA->getAs(); + auto *PB = QB->getAs(); + if (!PA || !PB) + return; + + // check inner pointer dimensions + TypeLoc APointee = getPointeeTypeLoc(A); + TypeLoc BPointee = getPointeeTypeLoc(B); + if (APointee && BPointee) { + fixBoundsSafetyTypeLocs(S, D, APointee, BPointee, PA->getPointeeType(), + PB->getPointeeType(), ADecl, BDecl); + } + + // And then try to adjust this pointer. No adjustments to make if B already + // has attributes. + // We do not touch the pointer if is __single or __unsafe_indexable, because + // PointerTypeLoc cannot tell us whether this was added implicitly or + // explicitly. when it is added explicitly, the fixit could suggest incorrect + // code, like `T *__bidi_indexable __single`. It's also technically a + // possibility for __indexable and __bidi_indexable, but using those as the + // default pointer attribute is not currently a common use case (although we + // think this could change). + if (QB->getAs() || + QB->getAs() || + !PB->getPointerAttributes().hasRawPointerLayout()) + return; + + auto GetEndPointer = [](QualType QT) -> Expr * { + if (auto DRPT = QT->getAs()) + return DRPT->getEndPointer(); + return nullptr; + }; + + std::string BoundsAnnotation; + llvm::raw_string_ostream KS(BoundsAnnotation); + if (PA->getPointerAttributes().isIndexable()) { + KS << "__indexable"; + } else if (PA->getPointerAttributes().isBidiIndexable()) { + KS << "__bidi_indexable"; + } else if (auto DCPTA = QA->getAs()) { + std::string Rewritten; + if (!TransposeDynamicBoundsExpr::Rewrite(S, BDecl, DCPTA->getCountExpr(), + Rewritten)) + return; + const char *Keyword = + DCPTA->isCountInBytes() ? "__sized_by" : "__counted_by"; + KS << Keyword; + if (DCPTA->isOrNull()) + KS << "_or_null"; + KS << '(' << Rewritten << ')'; + } else if (auto EndPointer = GetEndPointer(QA)) { + std::string Rewritten; + if (!TransposeDynamicBoundsExpr::Rewrite(S, BDecl, EndPointer, Rewritten)) + return; + KS << "__ended_by(" << Rewritten << ')'; + } else { + return; + } + + SourceLocation FixItLoc; + bool AddSpace = false; + std::tie(FixItLoc, AddSpace) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(B, S); + if (FixItLoc.isInvalid()) + return; + + if (AddSpace) + KS << " "; + + KS.flush(); + D << FixItHint::CreateInsertion(FixItLoc, BoundsAnnotation); +} + +static void fixBoundsSafetyFunctionDecl(Sema &S, Sema::SemaDiagnosticBuilder &D, + FunctionDecl *FromDecl, + FunctionDecl *ToDecl) { + if (FromDecl->getNumParams() != ToDecl->getNumParams()) + return; + + auto FromTL = FromDecl->getFunctionTypeLoc(); + auto ToTL = ToDecl->getFunctionTypeLoc(); + if (!FromTL || !ToTL) + return; + + fixBoundsSafetyTypeLocs(S, D, ToTL.getReturnLoc(), FromTL.getReturnLoc(), + ToDecl->getReturnType(), FromDecl->getReturnType(), + ToDecl, FromDecl); + for (unsigned I = 0; I < FromTL.getNumParams(); ++I) { + auto FromParam = FromTL.getParam(I); + auto ToParam = ToTL.getParam(I); + auto FromParamTL = FromParam->getTypeSourceInfo()->getTypeLoc(); + auto ToParamTL = ToParam->getTypeSourceInfo()->getTypeLoc(); + fixBoundsSafetyTypeLocs(S, D, ToParamTL, FromParamTL, ToParam->getType(), + FromParam->getType(), ToDecl, FromDecl); + } +} + +/// diagnoseFunctionConflictWithDynamicBoundTypes - diagnose if \p New +/// and \p Old have conflicting return or parameter types with repect to +/// '__counted_by', '__sized_by', or '__ended_by' attributes. +static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, + FunctionDecl *Old, + Sema &Self) { + std::function checkCompatibleBoundExprs; + checkCompatibleBoundExprs = [&](const Expr *NewExpr, const Expr *OldExpr) -> bool { + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + if (NewExpr) + NewExpr->Profile(NewID, Self.Context, /*Canonical*/ true); + if (OldExpr) + OldExpr->Profile(OldID, Self.Context, /*Canonical*/ true); + if (NewID == OldID) + return true; + return false; + }; + std::unique_ptr D; + std::function + checkCompatibleDynamicBoundTypes = + [&](QualType NewTy, QualType OldTy, TypeLoc NewTL, TypeLoc OldTL, + SourceLocation NewLoc, SourceLocation OldLoc) -> bool { + if (!NewTy->isPointerType() || !OldTy->isPointerType()) + return true; + const auto *NewDCPTy = NewTy->getAs(); + const auto *OldDCPTy = OldTy->getAs(); + const auto *NewDRPTy = NewTy->getAs(); + const auto *OldDRPTy = OldTy->getAs(); + const auto *NewVTTy = NewTy->getAs(); + const auto *OldVTTy = OldTy->getAs(); + const unsigned CountDiagIndex = 0; + const unsigned SizeDiagIndex = 1; + const unsigned CountOrNullDiagIndex = 2; + const unsigned SizeOrNullDiagIndex = 3; + const unsigned RangeDiagIndex = 4; + const unsigned TermDiagIndex = 5; + + auto ReportConflict = [&](const unsigned Index) { + { + if (!D) + D.reset(new Sema::SemaDiagnosticBuilder( + Self.Diag(NewLoc, diag::err_bounds_safety_dynamic_bound_redeclaration) + << Index << 0)); + fixBoundsSafetyTypeLocs(Self, *D, NewTL, OldTL, NewTy, OldTy, New, Old); + fixBoundsSafetyTypeLocs(Self, *D, OldTL, NewTL, OldTy, NewTy, Old, New); + } + }; + auto ReportCountConflict = [&](const CountAttributedType *DCPTy) { + unsigned Index; + switch (DCPTy->getKind()) { + case CountAttributedType::CountedBy: + Index = CountDiagIndex; + break; + case CountAttributedType::CountedByOrNull: + Index = CountOrNullDiagIndex; + break; + case CountAttributedType::SizedBy: + Index = SizeDiagIndex; + break; + case CountAttributedType::SizedByOrNull: + Index = SizeOrNullDiagIndex; + break; + default: + llvm_unreachable("Unexpected BoundsAttrKind"); + } + ReportConflict(Index); + }; + + if (NewDCPTy && !OldDCPTy) { + ReportCountConflict(NewDCPTy); + return false; + } + + if (NewDRPTy && !OldDRPTy) { + ReportConflict(RangeDiagIndex); + return false; + } + + if (NewVTTy && !OldVTTy && + !OldTy->isUnspecifiedPointerType()) { + ReportConflict(TermDiagIndex); + return false; + } + + if (!NewDCPTy && OldDCPTy) { + ReportCountConflict(OldDCPTy); + return false; + } + // Implicit __started_by attributes have not yet been added to NewTy, so + // don't diagnose that. + if (!NewDRPTy && OldDRPTy && OldDRPTy->getEndPointer()) { + ReportConflict(RangeDiagIndex); + return false; + } + + if (!NewVTTy && OldVTTy) { + ReportConflict(TermDiagIndex); + return false; + } + + if (NewDCPTy && OldDCPTy) { + if (!checkCompatibleBoundExprs(NewDCPTy->getCountExpr(), OldDCPTy->getCountExpr())) { + ReportCountConflict(NewDCPTy); + return false; + } + // '__sized_by' and '__counted_by' + if (NewDCPTy->isCountInBytes() != OldDCPTy->isCountInBytes()) { + CharUnits NewPointeeSize = Self.Context.getTypeSizeInChars(NewDCPTy->getPointeeType()); + if (!NewPointeeSize.isOne() && !NewDCPTy->isVoidPointerType()) { + ReportCountConflict(NewDCPTy); + return false; + } + } + // '__counted_by_or_null' and '__counted_by' + if (NewDCPTy->isOrNull() != OldDCPTy->isOrNull()) { + ReportCountConflict(NewDCPTy); + return false; + } + } + if (NewDRPTy && OldDRPTy) { + // Start pointers are implicit so we don't check it here. + if (!checkCompatibleBoundExprs(NewDRPTy->getEndPointer(), OldDRPTy->getEndPointer())) { + ReportConflict(RangeDiagIndex); + return false; + } + } + if (NewVTTy && OldVTTy && + !llvm::APSInt::isSameValue(NewVTTy->getTerminatorValue(Self.Context), + OldVTTy->getTerminatorValue(Self.Context))) { + ReportConflict(TermDiagIndex); + return false; + } + TypeLoc NewSubTL, OldSubTL; + if (NewTL && OldTL) { + auto NewPTL = NewTL.getAs(); + auto OldPTL = OldTL.getAs(); + if (NewPTL && OldPTL) { + NewSubTL = NewPTL.getPointeeLoc(); + OldSubTL = OldPTL.getPointeeLoc(); + } + } + return checkCompatibleDynamicBoundTypes(NewTy->getPointeeType(), + OldTy->getPointeeType(), NewSubTL, + OldSubTL, NewLoc, OldLoc); + }; + + TypeLoc OldReturnTL, NewReturnTL; + // OldTL or NewTL will be missing if the function was declared without using + // function syntax, for instance with `typeof(snprintf) mysnprintf;`. + if (auto OldTL = Old->getFunctionTypeLoc()) + OldReturnTL = OldTL.getReturnLoc(); + if (auto NewTL = New->getFunctionTypeLoc()) + NewReturnTL = NewTL.getReturnLoc(); + bool Compatible = checkCompatibleDynamicBoundTypes( + New->getReturnType(), Old->getReturnType(), NewReturnTL, OldReturnTL, + New->getBeginLoc(), Old->getBeginLoc()); + + auto MinNumParams = std::min(New->getNumParams(), Old->getNumParams()); + for (unsigned i = 0; i < MinNumParams; ++i) { + auto *OldParam = Old->getParamDecl(i); + auto *NewParam = New->getParamDecl(i); + Compatible &= checkCompatibleDynamicBoundTypes( + NewParam->getType(), OldParam->getType(), + NewParam->getTypeSourceInfo()->getTypeLoc(), + OldParam->getTypeSourceInfo()->getTypeLoc(), + NewParam->getBeginLoc(), OldParam->getBeginLoc()); + } + + if (D) { + D.reset(); + Self.Diag(Old->getBeginLoc(), diag::note_previous_declaration); + } + + // New->getNumParams() != Old->getNumParams() should be handled in the other logic. + return Compatible; +} + +struct RebuildDynamicBoundsExpr + : public TreeTransform { + using BaseClass = TreeTransform; + FunctionDecl *New; + RebuildDynamicBoundsExpr(Sema &SemaRef, FunctionDecl *New) + : BaseClass(SemaRef), New(New) {} + bool AlwaysRebuild() const { return true; } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + assert(isa(E->getDecl())); + auto *OldParm = cast(E->getDecl()); + auto *NewParm = New->getParamDecl(OldParm->getFunctionScopeIndex()); + DeclarationNameInfo NewNameInfo; + NewNameInfo.setName(NewParm->getDeclName()); + assert(!E->hasExplicitTemplateArgs()); + + // Temporarily put parameter variables in the translation unit, not the + // enclosing context. This is what Sema::ActOnParamDeclarator() already + // does when declaring a function. In BoundsSafety, we do it to allow + // creating references to the parameters when rebuilding the count + // expression. Otherwise, creating such references triggers an error. + // TODO: Check how this works for C++ methods and such. + DeclContext *DC = NewParm->getDeclContext(); + if (isa(DC)) + NewParm->setDeclContext(SemaRef.Context.getTranslationUnitDecl()); + ExprResult Res = RebuildDeclRefExpr(NewParm->getQualifierLoc(), NewParm, + NewNameInfo, NewParm, nullptr); + NewParm->setDeclContext(DC); + + return Res; + } +}; + +/// mergeFunctionDeclBoundsAttributes - merges +/// '__counted_by/__sized_by/__ended_by' attributes in \p Old to \p New. Returns +/// true if \p New has been updated. This doesn't diagnose conflicting +/// attributes but it merges those attributes on a best efforts basis. If the +/// merge was unsuccessful, the function declarations will remain incompatible +/// so they will be caught by following diagnostics in Sema::MergeFunctionDecl. +static bool mergeFunctionDeclBoundsAttributes(FunctionDecl *New, + FunctionDecl *Old, Sema &Self) { + if (New->getNumParams() != Old->getNumParams()) + return false; + + RebuildDynamicBoundsExpr ExprBuilder(Self, New); + std::function + mergeBoundsAttributes; + mergeBoundsAttributes = [&](QualType NewTy, QualType OldTy, + QualType MergePointeeTy, + QualType OrigNewTy) -> QualType { + if (OldTy.isNull()) + return QualType(); + if (MergePointeeTy.isNull()) + return QualType(); + if (NewTy->isBoundsAttributedType() || NewTy->isValueTerminatedType()) + return QualType(); + + // mergeBoundsSafetyPointerTypes removes any AttributedTypes from NewTy, + // calls mergeBoundsAttributes to merge the attributes at each level, and + // then reapplies the AttributedTypes to the merged type. OrigNewTy is the + // same as NewTy but without dropping the AttributedTypes. This allows us + // to check if the pointer has any attributes at all (such as __single + // applied using AttributedType). + if (Self.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !OrigNewTy->isUnspecifiedPointerType()) + return QualType(); + + BoundsSafetyPointerAttributes Attributes; + if (auto *NewPTy = NewTy->getAs()) + Attributes = NewPTy->getPointerAttributes(); + if (Attributes.isUnspecified()) + if (auto *OldPTy = NewTy->getAs()) + Attributes = OldPTy->getPointerAttributes(); + + QualType MergeTy = Self.Context.getPointerType(MergePointeeTy, Attributes); + if (auto *OldDCPTy = OldTy->getAs()) { + ExprResult NewCount = ExprBuilder.TransformExpr(OldDCPTy->getCountExpr()); + if (NewCount.isInvalid()) + return QualType(); + return Self.BuildCountAttributedType(MergeTy, NewCount.get(), + OldDCPTy->isCountInBytes(), + OldDCPTy->isOrNull()); + } + if (auto *OldDRPTy = OldTy->getAs()) { + Expr *StartPtr = OldDRPTy->getStartPointer(); + Expr *EndPtr = OldDRPTy->getEndPointer(); + if (StartPtr) { + ExprResult NewStart = ExprBuilder.TransformExpr(StartPtr); + if (NewStart.isInvalid()) + return QualType(); + StartPtr = NewStart.get(); + } + if (EndPtr) { + ExprResult NewEnd = + ExprBuilder.TransformExpr(OldDRPTy->getEndPointer()); + if (NewEnd.isInvalid()) + return QualType(); + EndPtr = NewEnd.get(); + } + return Self.BuildDynamicRangePointerType(MergeTy, StartPtr, EndPtr); + } + return MergeTy; + }; + QualType NewRetTy = New->getReturnType(); + if (NewRetTy->isBoundsAttributedType() || + NewRetTy->isValueTerminatedType()) + return false; + QualType MergeRetTy = Self.Context.mergeBoundsSafetyPointerTypes( + New->getReturnType(), Old->getReturnType(), mergeBoundsAttributes); + if (MergeRetTy.isNull()) + return false; + + llvm::SmallVector MergeParamTys; + for (unsigned i = 0; i < New->getNumParams(); ++i) { + QualType NewParmTy = New->getParamDecl(i)->getType(); + if (NewParmTy->isBoundsAttributedType() || + NewParmTy->isValueTerminatedType()) + return false; + QualType MergeParamTy = Self.Context.mergeBoundsSafetyPointerTypes( + NewParmTy, Old->getParamDecl(i)->getType(), + mergeBoundsAttributes); + if (MergeParamTy.isNull()) + return false; + if (const auto *CATy = MergeParamTy->getAs()) { + Self.AttachDependerDeclsAttr(New->getParamDecl(i), CATy, /*Level*/ 0); + } + MergeParamTys.push_back(MergeParamTy); + } + bool Updated = MergeRetTy != New->getReturnType(); + for (unsigned i = 0; i < New->getNumParams(); ++i) { + auto *NewParm = New->getParamDecl(i); + if (NewParm->getType() != MergeParamTys[i]) { + New->getParamDecl(i)->setType(MergeParamTys[i]); + Updated = true; + } + } + if (Updated) { + if (auto *FT = New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionType( + MergeRetTy, MergeParamTys, FT->getExtProtoInfo()); + New->setType(NewFuncTy); + } else if (New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionNoProtoType(MergeRetTy); + New->setType(NewFuncTy); + } + } + return Updated; +} + +static bool mergeFunctionDeclTerminatedByAttribute(FunctionDecl *New, + FunctionDecl *Old, Sema &Self) { + if (New->getNumParams() != Old->getNumParams()) + return false; + + ASTContext &Ctx = Self.Context; + SourceLocation NewLoc = New->getLocation(); + SourceLocation OldLoc = Old->getLocation(); + auto ReportConflict = [&]() { + Self.Diag(NewLoc, diag::err_bounds_safety_dynamic_bound_redeclaration) + << /*terminated_by_*/ 5 << 0; + Self.Diag(OldLoc, diag::note_previous_declaration); + }; + + std::function + mergeTerminatedByAttributes = [&](QualType NewTy, QualType OldTy, + QualType MergePointeeTy, + QualType OrigNewTy) -> QualType { + if (OldTy.isNull()) + return QualType(); + if (MergePointeeTy.isNull()) + return QualType(); + if (NewTy->isBoundsAttributedType() || + OldTy->isBoundsAttributedType()) + return QualType(); + + BoundsSafetyPointerAttributes Attributes; + const auto *NewVTTy = NewTy->getAs(); + const auto *OldVTTy = OldTy->getAs(); + if (NewVTTy && !OldVTTy) { + // Take the new type attribute + if (OldTy->isUnspecifiedPointerType()) { + Attributes = NewTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + NewVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (!NewVTTy && OldVTTy) { + // Take the old type attribute + if (NewTy->isUnspecifiedPointerType()) { + Attributes = OldTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + OldVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (NewVTTy && OldVTTy) { + if (llvm::APSInt::isSameValue(NewVTTy->getTerminatorValue(Ctx), + OldVTTy->getTerminatorValue(Ctx))) { + Attributes = NewTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + NewVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (auto *NewPTy = NewTy->getAs()) + Attributes = NewPTy->getPointerAttributes(); + if (Attributes.isUnspecified()) + if (auto *OldPTy = OldTy->getAs()) + Attributes = OldPTy->getPointerAttributes(); + + return Ctx.getPointerType(MergePointeeTy, Attributes); + }; + + if (New->getReturnType()->isBoundsAttributedType()) + return false; + QualType MergeRetTy = Ctx.mergeBoundsSafetyPointerTypes( + New->getReturnType(), Old->getReturnType(), mergeTerminatedByAttributes); + if (MergeRetTy.isNull()) + return false; + + llvm::SmallVector MergeParamTys; + for (unsigned i = 0; i < New->getNumParams(); ++i) { + NewLoc = New->getParamDecl(i)->getLocation(); + OldLoc = Old->getParamDecl(i)->getLocation(); + QualType NewParmTy = New->getParamDecl(i)->getType(); + if (NewParmTy->isBoundsAttributedType()) + return false; + QualType MergeParamTy = Self.Context.mergeBoundsSafetyPointerTypes( + NewParmTy, Old->getParamDecl(i)->getType(), + mergeTerminatedByAttributes); + if (MergeParamTy.isNull()) + return false; + MergeParamTys.push_back(MergeParamTy); + } + bool Updated = MergeRetTy != New->getReturnType(); + for (unsigned i = 0; i < New->getNumParams(); ++i) { + auto *NewParm = New->getParamDecl(i); + if (NewParm->getType() != MergeParamTys[i]) { + New->getParamDecl(i)->setType(MergeParamTys[i]); + Updated = true; + } + } + if (Updated) { + if (auto *FT = New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionType( + MergeRetTy, MergeParamTys, FT->getExtProtoInfo()); + New->setType(NewFuncTy); + } else if (auto *FT = New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionNoProtoType(MergeRetTy); + New->setType(NewFuncTy); + } + } + return Updated; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, bool MergeTypeWithOld, bool NewDeclIsDefn) { // Verify the old decl was also a function. @@ -3813,6 +4481,29 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, return true; } + /*TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().BoundsSafetyAttributes) { + // This is the same logic to suppress warnings in system headers. + // In case of system functions, we want to inherit counted_by attributes + // from the old declaration. + if (!Old->isImplicit() && + !getSourceManager().isInSystemHeader(New->getLocation()) && + !getSourceManager().isInSystemHeader(Old->getLocation())) { + if (!diagnoseFunctionConflictWithDynamicBoundTypes(New, Old, *this)) + return true; + } else if (mergeFunctionDeclBoundsAttributes(New, Old, *this)) { + NewQType = New->getType(); + } + } + + if (getLangOpts().BoundsSafety && + mergeFunctionDeclTerminatedByAttribute(New, Old, *this)) { + // TODO: Merge __terminated_by() attributes in attribute-only mode. + // rdar://137984921 + NewQType = New->getType(); + } + /*TO_UPSTREAM(BoundsSafety) OFF */ + QualType OldQTypeForComparison = OldQType; if (Context.hasAnyFunctionEffects()) { const auto OldFX = Old->getFunctionEffects(); @@ -4266,7 +4957,16 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, PrevDiag = diag::note_previous_builtin_declaration; } - Diag(New->getLocation(), diag::err_conflicting_types) << New->getDeclName(); + { + auto ConflictDiag = Diag(New->getLocation(), diag::err_conflicting_types) + << New->getDeclName(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + fixBoundsSafetyFunctionDecl(*this, ConflictDiag, New, Old); + fixBoundsSafetyFunctionDecl(*this, ConflictDiag, Old, New); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } Diag(OldLocation, PrevDiag) << Old << Old->getType(); return true; } @@ -6859,6 +7559,53 @@ void Sema::deduceOpenCLAddressSpace(ValueDecl *Decl) { } } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void Sema::deduceBoundsSafetyPointerTypes(ValueDecl *Decl) { + bool ShouldAutoBound = false; + if (VarDecl *Var = dyn_cast(Decl)) { + // Bound automatically only the local variables that have local storage. + // Unspecified local variables with global storage (e.g. static local) + // should get the default ABI attributes. + ShouldAutoBound = Var->isLocalVarDecl() && Var->hasLocalStorage(); + } + Decl->setType(Context.getBoundsSafetyAutoPointerType( + Decl->getType(), CurPointerAbi, ShouldAutoBound)); +} + +static QualType deduceBoundsSafetyFuncType(Sema &S, const FunctionDecl *Decl, + QualType Ty) { + if (const auto *AttrTy = Ty->getAs()) { + QualType ModTy = + deduceBoundsSafetyFuncType(S, Decl, AttrTy->getModifiedType()); + QualType EquivTy = + deduceBoundsSafetyFuncType(S, Decl, AttrTy->getEquivalentType()); + return S.Context.getAttributedType(AttrTy->getAttrKind(), ModTy, EquivTy); + } + + QualType NewRetTy = S.Context.getBoundsSafetyAutoPointerType( + Decl->getReturnType(), S.CurPointerAbi, + /*ShouldAutoBound=*/false); + + if (NewRetTy == Decl->getReturnType()) + return Ty; + + const FunctionType *FuncTy = Decl->getFunctionType(); + + if (const auto *FuncNoProtoTy = dyn_cast(FuncTy)) + return S.Context.getFunctionNoProtoType(NewRetTy, + FuncNoProtoTy->getExtInfo()); + + const auto *FuncProtoTy = cast(FuncTy); + return S.Context.getFunctionType(NewRetTy, FuncProtoTy->getParamTypes(), + FuncProtoTy->getExtProtoInfo()); +} + +void Sema::deduceBoundsSafetyFunctionTypes(FunctionDecl *Decl) { + QualType NewTy = deduceBoundsSafetyFuncType(*this, Decl, Decl->getType()); + Decl->setType(NewTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { // Ensure that an auto decl is deduced otherwise the checks below might cache // the wrong linkage. @@ -7101,6 +7848,50 @@ static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl, } } +static void checkExternalBoundsRedeclaration(Sema &S, Decl *D) { + VarDecl *NewVD = dyn_cast(D); + if (!NewVD || NewVD->isFirstDecl()) + return; + + if (NewVD->isFirstDecl()) + return; + VarDecl *PrevVD = NewVD->getFirstDecl(); + + const auto *PrevTy = PrevVD->getType()->getAs(); + const auto *NewTy = NewVD->getType()->getAs(); + + if (!PrevTy && !NewTy) + return; + + auto DiagConflict = [&](unsigned Kind) { + S.Diag(NewVD->getLocation(), + diag::err_bounds_safety_dynamic_bound_redeclaration) + << Kind << 1; + S.Diag(PrevVD->getLocation(), diag::note_previous_decl) << PrevVD; + }; + + if (PrevTy && !NewTy && PrevVD->hasExternalStorage() && + NewVD->getType()->isConstantArrayType()) { + return; // rdar://129246717: remove this workaround + } + + if ((bool)PrevTy != (bool)NewTy) { + unsigned Kind = NewTy ? NewTy->getKind() : PrevTy->getKind(); + return DiagConflict(Kind); + } + + assert(PrevTy && NewTy); + if (PrevTy->getKind() != NewTy->getKind()) + return DiagConflict(NewTy->getKind()); + + llvm::FoldingSetNodeID PrevID; + llvm::FoldingSetNodeID NewID; + PrevTy->getCountExpr()->Profile(PrevID, S.Context, /*Canonical*/ true); + NewTy->getCountExpr()->Profile(NewID, S.Context, /*Canonical*/ true); + if (PrevID != NewID) + return DiagConflict(NewTy->getKind()); +} + /// Given that we are within the definition of the given function, /// will that definition behave like C99's 'inline', where the /// definition is discarded except for optimization purposes? @@ -7831,6 +8622,11 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(NewVD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // WebAssembly tables are always in address space 1 (wasm_var). Don't apply // address space if the table has local storage (semantic checks elsewhere // will produce an error anyway). @@ -10243,6 +11039,32 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Finally, we know we have the right number of parameters, install them. NewFD->setParams(Params); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + // This is a good place to make sure that no array parameter has decayed + // to a __single pointer. Annoyingly, we can't do this when parameters are + // created because BoundsSafety count attributes are late-parsed and applied + // after. + for (ParmVarDecl *Param : Params) { + auto *TSInfo = Param->getTypeSourceInfo(); + QualType TST = TSInfo->getType(); + if (!Context.getAsArrayType(TST) || + TST->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) + continue; // nothing to check + if (Param->getType()->isCountAttributedType() || + Param->getType()->isDynamicRangePointerType()) + continue; // has a dynamic count, no problem + auto FA = Param->getType()->getAs()->getPointerAttributes(); + if (FA.isSingle()) { + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::err_bounds_safety_array_decay_to_single) << TST; + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::note_bounds_safety_array_decay_use_count_annotation); + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (D.getDeclSpec().isNoreturnSpecified()) NewFD->addAttr( C11NoReturnAttr::Create(Context, D.getDeclSpec().getNoreturnSpecLoc())); @@ -10314,6 +11136,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyFunctionTypes(NewFD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (!getLangOpts().CPlusPlus) { // Perform semantic checking on the function declaration. if (!NewFD->isInvalidDecl() && NewFD->isMain()) @@ -13576,6 +14403,22 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { } Init = Result.get(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety prevents passing flexible array members by copy. We still allow + // initializer lists (and no initializers), but that's it. + if (Init && getLangOpts().BoundsSafety && VDecl->hasLocalStorage()) { + auto *RecordT = VDecl->getType()->getAs(); + if (RecordT && RecordT->getDecl()->hasFlexibleArrayMember()) { + if (!isa(Init->IgnoreParenImpCasts())) { + Diag( + Init->getBeginLoc(), diag::err_flexible_array_member_passed_by_copy) + << Init->IgnoreParenImpCasts()->getType(); + // Recover by pretending this worked + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Attach the initializer to the decl. VDecl->setInit(Init); @@ -13826,6 +14669,11 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { if (VarDecl *Var = dyn_cast(RealDecl)) { QualType Type = Var->getType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + CheckValueTerminatedUninitialized(Var); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory. if (isa(RealDecl)) { Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var; @@ -14705,6 +15553,39 @@ static bool hasDeducedAuto(DeclaratorDecl *DD) { return VD && !VD->getType()->hasAutoForTrailingReturnType(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static void checkAtomicAutoPointerAttrs(Sema &S, const Decl *D) { + const auto *VD = dyn_cast(D); + if (!VD) + return; + QualType Ty = VD->getType(); + for (;;) { + // If we auto bound an atomic pointer, we should have: + // AtomicType -> AttributedType -> PointerType. + bool HasAutoAttr = false; + if (const auto *AtomTy = Ty->getAs()) { + Ty = AtomTy->getValueType(); + if (const auto *AttrTy = Ty->getAs()) { + if (AttrTy->getAttrKind() == attr::PtrAutoAttr) + HasAutoAttr = true; + Ty = AttrTy->getModifiedType(); + } + } + const auto *PtrTy = Ty->getAs(); + if (!PtrTy) + break; + if (HasAutoAttr && !PtrTy->isUnspecified() && !PtrTy->isUnsafeIndexable() && + !PtrTy->isSingle()) { + assert(PtrTy->isIndexable() || PtrTy->isBidiIndexable()); + unsigned DiagIndex = PtrTy->isIndexable() ? 0 : 1; + S.Diag(D->getBeginLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + } + Ty = PtrTy->getPointeeType(); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, ArrayRef Group) { SmallVector Decls; @@ -14764,6 +15645,33 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) + checkExternalBoundsRedeclaration(*this, D); + // Check if an atomic pointer was auto bound to __bidi_indexable, and if + // so, emit an error, as atomic __bidi_indexable pointers are currently + // not supported. We must perform this check after handling dynamic bound + // pointers, since their construction can replace pointer attributes. + if (getLangOpts().BoundsSafety) { + checkAtomicAutoPointerAttrs(*this, D); + if (auto Var = dyn_cast(D)) { + DiagnoseDynamicCountVarZeroInit(Var); + + // This check is done now and not earlier in + // `Sema::ActOnVariableDeclarator()` because in that method attributes + // haven't yet been applied due to late parsing. + // + // Tentative definitions are skipped because it isn't known if + // the VarDecl is a definition until the end of the translation unit. + // Tentative definitions are instead checked in + // `Sema::ActOnEndOfTranslationUnit()`. + if (!BoundsSafetyCheckVarDecl(Var, + /*CheckTentativeDefinitions=*/false)) + Var->setInvalidDecl(); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + Decls.push_back(D); } @@ -14990,6 +15898,13 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D, CheckParameter(Context.getTranslationUnitDecl(), D.getBeginLoc(), D.getIdentifierLoc(), II, parmDeclType, TInfo, SC); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // We reuse the same function 'deduceBoundsSafetyPointerTypes' that deduces + // -fbounds-safety pointer types of Fields and VarDecl. + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(New); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (D.isInvalidType()) New->setInvalidDecl(); @@ -15083,10 +15998,52 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, TypeSourceInfo *TSInfo, StorageClass SC) { // Perform Objective-C ARC adjustments. T = ObjC().AdjustParameterTypeForObjCAutoRefCount(T, NameLoc, TSInfo); + /*TO_UPSTREAM(BoundsSafety) ON*/ + QualType Adjusted = Context.getAdjustedParameterType(T); + + // Perform -fbounds-safety pointer type adjustments. + if (getLangOpts().BoundsSafety) { + auto *AT = Context.getAsArrayType(T); + if (AT && !T->isAnyVaListType(Context)) { + Expr *Count = nullptr; + if (!T->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + if (auto *CAT = dyn_cast(AT)) { + ArrayTypeLoc ATL = TSInfo->getTypeLoc().getAsAdjusted(); + Count = ATL.isNull() + ? new (Context) IntegerLiteral(Context, CAT->getSize(), + Context.getSizeType(), + SourceLocation()) + : ATL.getSizeExpr(); + } else if (auto *VAT = dyn_cast(AT)) { + Count = VAT->getSizeExpr(); + } else if (auto *DAT = dyn_cast(AT)) { + Count = DAT->getSizeExpr(); + } + } + if (Count) { + Adjusted = BuildCountAttributedType(Adjusted, Count, false); + } + } + + auto *RecordT = T->getAs(); + if (RecordT && RecordT->getDecl()->hasFlexibleArrayMember()) { + // BoundsSafety prevents passing flexible array members by copy. + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::err_flexible_array_member_passed_by_copy) << TSInfo->getType(); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, - Context.getAdjustedParameterType(T), - TSInfo, SC, nullptr); + Adjusted, TSInfo, SC, nullptr); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) { + if (const auto *CATy = New->getType()->getAs()) { + AttachDependerDeclsAttr(New, CATy, /*Level*/ 0); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ // Make a note if we created a new pack in the scope of a lambda, so that // we know that references to that pack must also be expanded within the @@ -15545,6 +16502,15 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, AbstractReturnType))) FD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!FD->isInvalidDecl()) { + // Note if the checks fail we don't make the decl invalid, + // otherwise we would stop diagnosing calls to this functions from other + // functions. + BoundsSafetyCheckReturnTyForFunctionDef(FD); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (FnBodyScope) PushDeclContext(FnBodyScope, FD); @@ -16221,6 +17187,11 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD && !FD->isDeleted()) checkTypeSupport(FD->getType(), FD->getLocation(), FD); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + DynamicCountPointerAssignmentAnalysis(*this, dcl).run(); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return dcl; } @@ -18511,6 +19482,11 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, PPC().CheckPPCMMAType(T, NewFD->getLocation())) NewFD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(NewFD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + NewFD->setAccess(AS); return NewFD; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 7be1c31399520..9b843dee89fa2 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -23,6 +23,7 @@ #include "clang/AST/Mangle.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeVisitor.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Cuda.h" #include "clang/Basic/DarwinSDKInfo.h" @@ -71,6 +72,8 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Triple.h" +#include "TreeTransform.h" +#include "DynamicCountPointerAssignmentAnalysis.h" #include using namespace clang; @@ -1217,35 +1220,11 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) { << TT->getDecl(); } -bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) { - if (RefOkay) { - if (T->isReferenceType()) - return true; - } else { - T = T.getNonReferenceType(); - } - - // The nonnull attribute, and other similar attributes, can be applied to a - // transparent union that contains a pointer type. - if (const RecordType *UT = T->getAsUnionType()) { - if (UT && UT->getDecl()->hasAttr()) { - RecordDecl *UD = UT->getDecl(); - for (const auto *I : UD->fields()) { - QualType QT = I->getType(); - if (QT->isAnyPointerType() || QT->isBlockPointerType()) - return true; - } - } - } - - return T->isAnyPointerType() || T->isBlockPointerType(); -} - static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL, SourceRange AttrParmRange, SourceRange TypeRange, bool isReturnValue = false) { - if (!S.isValidPointerAttrType(T)) { + if (!isValidPointerAttrType(T)) { if (isReturnValue) S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL << AttrParmRange << TypeRange; @@ -1254,6 +1233,13 @@ static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL, << AL << AttrParmRange << TypeRange << 0; return false; } + if (auto DCPTy = T->getAs()) { + if (DCPTy->isOrNull()) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << DCPTy->isCountInBytes() << AL << AttrParmRange << TypeRange; + } + } return true; } @@ -1286,8 +1272,19 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { QualType T = getFunctionOrMethodParamType(D, I); - if (T->isDependentType() || S.isValidPointerAttrType(T)) + if (T->isDependentType() || isValidPointerAttrType(T)) { AnyPointers = true; + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (auto DCPTy = T->getAs()) { + if (DCPTy->isOrNull()) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << DCPTy->isCountInBytes() << AL + << getFunctionOrMethodParamRange(D, I); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } } if (!AnyPointers) @@ -1334,10 +1331,11 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (D->isInvalidDecl()) return; - // noescape only applies to pointer types. + // noescape only applies to pointer and record types. QualType T = cast(D)->getType(); - if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { - S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only) + if (!isValidPointerAttrType(T, /* RefOkay */ true) && !T->isRecordType()) { + S.Diag(AL.getLoc(), + diag::warn_attribute_pointer_or_reference_or_record_only) << AL << AL.getRange() << 0; return; } @@ -1711,6 +1709,14 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ResultType = getFunctionOrMethodResultType(D); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Drop the restrict attribute for wide pointers. + // __indexable/__bidi_indexable turn the pointer into an aggregate type. + // restrict/__attribute__((malloc)) implies noalias attribute in LLVM IR, + // which is invalid on aggregate types. + if (ResultType->isPointerTypeWithBounds()) + return; + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (ResultType->isAnyPointerType() || ResultType->isBlockPointerType()) { D->addAttr(::new (S.Context) RestrictAttr(S.Context, AL)); return; @@ -5362,6 +5368,1292 @@ static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { XRayLogArgsAttr(S.Context, AL, ArgCount.getSourceIndex())); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +namespace { + +template +class ConstructDynamicBoundType + : public TypeVisitor { + using BaseClass = TypeVisitor; + + Derived *getDerived() { + return static_cast(this); + } + +protected: + Sema &S; + const StringRef DiagName; + Expr *ArgExpr; + SourceLocation Loc; + const BoundsAttributedType *ConstructedType = nullptr; + unsigned Level; + bool ScopeCheck; + bool AllowRedecl; + bool AutoPtrAttributed = false; + bool AtomicErrorEmitted = false; + +public: + explicit ConstructDynamicBoundType(Sema &S, unsigned Level, + const StringRef DiagName, Expr *ArgExpr, + SourceLocation Loc, bool ScopeCheck, + bool AllowRedecl) + : S(S), DiagName(DiagName), ArgExpr(ArgExpr), Loc(Loc), Level(Level), + ScopeCheck(ScopeCheck), AllowRedecl(AllowRedecl) {} + + QualType Visit(QualType T) { + SplitQualType SQT = T.split(); + QualType InnerTy = BaseClass::Visit(SQT.Ty); + if (InnerTy.isNull()) + return QualType(); + return S.Context.getQualifiedType(InnerTy, SQT.Quals); + } + + bool hadAtomicError() const { return AtomicErrorEmitted; } + const BoundsAttributedType *getConstructedType() const { + return ConstructedType; + } + + QualType VisitType(const Type *T) { + if (const auto *PTy = T->getAs()) + return VisitPointerType(PTy); + S.Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0; + return QualType(); + } + + QualType VisitParenType(const ParenType *T) { + QualType InnerTy = Visit(T->getInnerType()); + return S.Context.getParenType(InnerTy); + } + + QualType VisitFunctionProtoType(const FunctionProtoType *FPT) { + // The attribute applies to the return type. + QualType QT = Visit(FPT->getReturnType()); + if (QT.isNull()) + return QualType(); + + return S.Context.getFunctionType(QT, + FPT->getParamTypes(), + FPT->getExtProtoInfo()); + } + + QualType VisitFunctionNoProtoType(const FunctionNoProtoType *FPT) { + // The attribute applies to the return type. + QualType QT = Visit(FPT->getReturnType()); + if (QT.isNull()) + return QualType(); + + return S.Context.getFunctionNoProtoType(QT, FPT->getExtInfo()); + } + + QualType BuildDynamicBoundType(QualType CanonTy) { return QualType(); } + + QualType VisitPointerType(const PointerType *T) { + BoundsSafetyPointerAttributes FAttr = T->getPointerAttributes(); + + if (FAttr.hasUpperBound() && !AutoPtrAttributed) { + S.Diag(Loc, diag::err_bounds_safety_conflicting_count_bound_attributes) + << DiagName << (FAttr.hasLowerBound() ? 0 : 1); + return QualType(); + } + + if (Level == 0) { + if (S.getLangOpts().BoundsSafetyAttributes && !S.getLangOpts().BoundsSafety) + FAttr.setUnspecified(); + else + FAttr.setSingle(); + QualType QTy = (FAttr != T->getPointerAttributes()) + ? S.Context.getPointerType(T->getPointeeType(), FAttr) + : QualType(T, 0); + return getDerived()->BuildDynamicBoundType(QTy); + } + Level--; + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed, false); + QualType NewPointeeTy = Visit(T->getPointeeType()); + if (NewPointeeTy.isNull()) + return QualType(); + + if (T->getPointeeType() != NewPointeeTy) + return S.Context.getPointerType(NewPointeeTy, FAttr); + + return QualType(T, 0); + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { return QualType(); } + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { return QualType(); } + + QualType VisitCountAttributedType(const CountAttributedType *T) { + if (Level == 0) { + return getDerived()->DiagnoseConflictingType(T); + } + + QualType PointerTy = Visit(T->desugar()); + if (PointerTy.isNull()) + return QualType(); + + if (PointerTy != T->desugar()) { + return S.Context.getCountAttributedType( + PointerTy, T->getCountExpr(), T->isCountInBytes(), T->isOrNull(), + T->getCoupledDecls()); + } + return QualType(T, 0); + } + + QualType VisitDynamicRangePointerType(const DynamicRangePointerType *T) { + if (Level == 0) { + return getDerived()->DiagnoseConflictingType(T); + } + + QualType PointerTy = Visit(T->desugar()); + if (PointerTy.isNull()) + return QualType(); + + if (PointerTy != T->desugar()) { + return S.Context.getDynamicRangePointerType(PointerTy, T->getStartPointer(), + T->getEndPointer(), + T->getStartPtrDecls(), + T->getEndPtrDecls()); + } + return QualType(T, 0); + } + + QualType VisitMacroQualifiedType(const MacroQualifiedType *T) { + QualType NewTy = Visit(T->desugar()); + return S.Context.getMacroQualifiedType(NewTy, T->getMacroIdentifier()); + } + + QualType VisitArrayType(const ArrayType *T) { + return VisitType(T); + } + + QualType VisitAttributedType(const AttributedType *T) { + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed); + if (T->getAttrKind() == attr::PtrAutoAttr) + AutoPtrAttributed = true; + + QualType NewModTy = Visit(T->getModifiedType()); + if (NewModTy.isNull()) + return QualType(); + + if (T->getAttrKind() == attr::PtrAutoAttr) + return NewModTy; + + if (NewModTy != T->getModifiedType()) { + QualType NewEqTy = T->getModifiedType() != T->getEquivalentType() ? + Visit(T->getEquivalentType()) : NewModTy; + return S.Context.getAttributedType(T->getAttrKind(), NewModTy, NewEqTy); + } + return QualType(T, 0); + } + + QualType VisitValueTerminatedType(const ValueTerminatedType *T) { + if (Level == 0 && !AutoPtrAttributed) { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_wrong_pointer_type); + return QualType(); + } + QualType NewTy = Visit(T->desugar()); + // Drop ValueTerminatedType if there was an auto ptr attribute and we are + // making the current pointer a dynamic bound pointer. + if (Level == 0) + return NewTy; + return S.Context.getValueTerminatedType(NewTy, T->getTerminatorExpr()); + } +}; + +class ConstructCountAttributedType : + public ConstructDynamicBoundType { + bool CountInBytes; + bool OrNull; + +public: + explicit ConstructCountAttributedType(Sema &S, unsigned Level, + const StringRef DiagName, Expr *ArgE, + SourceLocation Loc, bool CountInBytes, + bool OrNull, bool AllowRedecl, + bool ScopeCheck = false) + : ConstructDynamicBoundType(S, Level, DiagName, ArgE, Loc, ScopeCheck, + AllowRedecl), + CountInBytes(CountInBytes), OrNull(OrNull) { + if (!ArgExpr->getType()->isIntegralOrEnumerationType()) { + S.Diag(Loc, diag::err_attribute_argument_type_for_bounds_safety_count) + << DiagName; + // Recover by using the integer literal 0 as the count. If we get to + // DefaultLvalueConversion and the count is itself a __counted_by value, + // clang will go down a fiery stack overflow. + ArgExpr = S.ActOnIntegerConstant(ArgExpr->getBeginLoc(), 0).get(); + } + } + + QualType BuildDynamicBoundType(QualType CanonTy) { + if (!CountInBytes && CanonTy->isPointerType()) { + auto PointeeTy = CanonTy->getPointeeType(); + // NOTE: We don't error for incomplete types that might be later completed + // (e.g. a struct forward declaration). Instead for those + // completable-incomplete types we delay checking until the type is used + // see `HasCountedByAttrOnIncompletePointee()`. This allows the counted_by + // attribute to be used on code that prefers to keep its pointees + // incomplete until they need to be used. + if (PointeeTy->isIncompletableIncompleteType() || + PointeeTy->isFunctionType() || PointeeTy->isSizelessType() || + PointeeTy->isStructureTypeWithFlexibleArrayMember()) { + // Use unspecified pointer attributes for diagnostic purposes. + QualType Unsp = S.Context.getBoundsSafetyPointerType( + CanonTy, BoundsSafetyPointerAttributes::unspecified()); + + auto PD = S.PDiag(diag::err_bounds_safety_counted_by_without_size); + PD << Unsp << Unsp->getPointeeType() << OrNull; + // Suggest `__sized_by` if the`__counted_by` macro was used. + // We intentionally don't suggest a fixit if the attribute is used + // directly (i.e. without the macro) because it is not expected that + // users will use it. + int SuggestFixIt = 0; // Default don't suggest __sized_by + if (Loc.isMacroID()) { + // FIXME(dliew): Use `AL.MacroII` to get the name. Unfortunately + // `AL.MacroII` is not set so we can't simply check the macro name is + // what we expect. So instead we have the lexer tell us the contents + // of the token and check against that. + // rdar://100631458 + auto MacroName = + Lexer::getImmediateMacroName(Loc, S.SourceMgr, S.LangOpts); + if (MacroName == "__counted_by") { + SuggestFixIt = 1; // Emit text to suggest __sized_by + auto MacroLoc = S.SourceMgr.getExpansionLoc(Loc); + PD << FixItHint::CreateReplacement(MacroLoc, "__sized_by"); + } else if (MacroName == "__counted_by_or_null") { + SuggestFixIt = 1; // Emit text to suggest __sized_by_or_null + auto MacroLoc = S.SourceMgr.getExpansionLoc(Loc); + PD << FixItHint::CreateReplacement(MacroLoc, "__sized_by_or_null"); + } + } + PD << SuggestFixIt; + S.Diag(Loc, PD); + + // Recover by assuming a byte count. + CountInBytes = true; + } + } + QualType Ty = S.BuildCountAttributedType(CanonTy, ArgExpr, CountInBytes, + OrNull, ScopeCheck); + assert(ConstructedType == nullptr); + ConstructedType = Ty->getAs(); + return Ty; + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { + if (AllowRedecl) { + QualType NewTy = BuildDynamicBoundType(T->desugar()); + const auto *NewDCPTy = NewTy->getAs(); + // We don't have a way to distinguish if '__counted_by' is conflicting or has been + // actually inherited from function declaration. Thus, we are now permitting + // redundant '__counted_by' as long as the resulting count expressions are equivalent + // and picking up the later one. + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + if (const auto *NewCnt = NewDCPTy->getCountExpr()) + NewCnt->Profile(NewID, S.Context, /*Canonical*/ true); + if (const auto *OldCnt = T->getCountExpr()) + OldCnt->Profile(OldID, S.Context, /*Canonical*/ true); + + if (NewID == OldID) + return NewTy; + } + + S.Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ T->isPointerType() << /* count */ 2; + return QualType(); + } + + QualType VisitFunctionProtoType(const FunctionProtoType *FPT) { + SaveAndRestore AllowRedeclLocal(AllowRedecl, true); + return ConstructDynamicBoundType::VisitFunctionProtoType(FPT); + } + + QualType VisitFunctionNoProtoType(const FunctionNoProtoType *FPT) { + SaveAndRestore AllowRedeclLocal(AllowRedecl, true); + return ConstructDynamicBoundType::VisitFunctionNoProtoType(FPT); + } + + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { + S.Diag(Loc, diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + QualType VisitArrayType(const ArrayType *T) { + if (Level == 0) { + if (T->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + return Visit(S.Context.getArrayDecayedType(QualType(T, 0))); + } else { + S.Diag(Loc, diag::err_bounds_safety_complete_array_with_count); + return QualType(); + } + } + return TraverseArrayType(T); + } + + QualType TraverseArrayType(const ArrayType *T) { + Level--; + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed, false); + QualType NewElementTy = Visit(T->getElementType()); + if (NewElementTy.isNull()) + return QualType(); + + if (T->getPointeeType() != NewElementTy) { + // Count attributes on the element of an array type are not supported yet + S.Diag(Loc, + diag::err_multiple_coupled_decls_in_bounds_safety_dynamic_count); + return QualType(); + } + + return QualType(T, 0); + } + + QualType VisitIncompleteArrayType(const IncompleteArrayType *T) { + if (Level == 0) { + if (CountInBytes) { + S.Diag(Loc, diag::err_bounds_safety_sized_by_array) << DiagName; + return QualType(); + } + + return BuildDynamicBoundType(QualType(T, 0)); + } + return TraverseArrayType(T); + } + + QualType VisitAtomicType(const AtomicType *T) { + QualType ValTy = T->getValueType(); + if (ValTy->isPointerType()) { + if (!AtomicErrorEmitted) { + unsigned DiagIndex = CountInBytes ? 3 : 2; + if (OrNull) + DiagIndex += 2; + S.Diag(Loc, diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + AtomicErrorEmitted = true; + } + // Construct the atomic pointer anyway. + QualType NewValTy = Visit(ValTy); + return S.Context.getAtomicType(NewValTy); + } + return VisitType(T); + } +}; + +class MakeStartedByPointerType + : public TreeTransform { + using BaseClass = TreeTransform; + bool Done = false; + unsigned Level; + TypeCoupledDeclRefInfo NewStartPtrInfo; + +public: + explicit MakeStartedByPointerType(Sema &SemaRef, unsigned Level, + TypeCoupledDeclRefInfo NewStartPtrInfo) + : BaseClass(SemaRef), Level(Level), NewStartPtrInfo(NewStartPtrInfo) {} + + Expr *BuildStartPtrExpr() const { + auto *StartVD = cast(NewStartPtrInfo.getDecl()); + Expr *StartPtr = SemaRef.BuildDeclRefExpr(StartVD, StartVD->getType(), + VK_LValue, SourceLocation()); + if (NewStartPtrInfo.isDeref()) { + StartPtr = + SemaRef.CreateBuiltinUnaryOp(SourceLocation(), UO_Deref, StartPtr) + .get(); + } + return StartPtr; + } + + QualType TransformType(TypeLocBuilder &TLB, TypeLoc TL) { + QualType T = TL.getType(); + + if (Level > 0 || T->isDynamicRangePointerType() || Done) + return BaseClass::TransformType(TLB, TL); + + if (T->isCountAttributedType()) { + SemaRef.Diag(TL.getBeginLoc(), + diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + llvm::SaveAndRestore DoneLocal(Done, true); + + T = BaseClass::TransformType(TLB, TL); + + // Create a new started_by() pointer. + + Expr *StartPtr = BuildStartPtrExpr(); + if (!StartPtr) + return QualType(); + + QualType NewTy = SemaRef.Context.getDynamicRangePointerType( + T, StartPtr, /*EndPtr=*/nullptr, {NewStartPtrInfo}, {}); + TLB.push(NewTy); + return NewTy; + } + + QualType TransformType(QualType T) { return BaseClass::TransformType(T); } + + TypeSourceInfo *TransformType(TypeSourceInfo *DI) { + return BaseClass::TransformType(DI); + } + + QualType TransformDynamicRangePointerType(TypeLocBuilder &TLB, + DynamicRangePointerTypeLoc TL) { + if (Level != 0 || Done) + return BaseClass::TransformDynamicRangePointerType(TLB, TL); + Done = true; + + // OldTy is an ended_by()/started_by() pointer, add a new start pointer and + // its decls. + + const DynamicRangePointerType *OldTy = TL.getTypePtr(); + + QualType InnerTy = BaseClass::TransformType(TLB, TL.getNextTypeLoc()); + + Expr *StartPtr = OldTy->getStartPointer(); + if (!StartPtr) { + StartPtr = BuildStartPtrExpr(); + if (!StartPtr) + return QualType(); + } + + llvm::SmallVector NewStartPtrDecls; + const auto &StartPtrDecls = OldTy->getStartPtrDecls(); + NewStartPtrDecls.reserve(StartPtrDecls.size() + 1); + NewStartPtrDecls.append(StartPtrDecls.begin(), StartPtrDecls.end()); + NewStartPtrDecls.push_back(NewStartPtrInfo.getDecl()); + + QualType NewTy = SemaRef.Context.getDynamicRangePointerType( + InnerTy, StartPtr, OldTy->getEndPointer(), NewStartPtrDecls, + OldTy->getEndPtrDecls()); + TLB.push(NewTy); + return NewTy; + } + + QualType TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL) { + llvm::SaveAndRestore LevelLocal(Level); + if (!Done) { + assert(Level > 0); + --Level; + } + return BaseClass::TransformPointerType(TLB, TL); + } +}; + +class ConstructDynamicRangePointerType : + public ConstructDynamicBoundType { + using BaseClass = ConstructDynamicBoundType; + std::optional StartPtrInfo; + +public: + explicit ConstructDynamicRangePointerType( + Sema &S, unsigned Level, const StringRef DiagName, Expr *ArgExpr, + SourceLocation Loc, bool AllowRedecl, bool ScopeCheck = false, + std::optional StartPtrInfo = std::nullopt) + : ConstructDynamicBoundType(S, Level, DiagName, ArgExpr, Loc, ScopeCheck, + AllowRedecl), + StartPtrInfo(StartPtrInfo) { + assert(ArgExpr->getType()->isPointerType()); + } + + QualType BuildDynamicBoundType(QualType CanonTy) { + QualType Ty = + S.BuildDynamicRangePointerType(CanonTy, nullptr, ArgExpr, ScopeCheck); + if (Ty.isNull()) + return QualType(); + + if (StartPtrInfo.has_value()) + MakeStartedByPointerTypes(cast(Ty)); + + assert(ConstructedType == nullptr); + ConstructedType = Ty->getAs(); + return Ty; + } + + void MakeStartedByPointerTypes(const DynamicRangePointerType *DRPT) const { + for (auto EndPtrInfo : DRPT->endptr_decls()) { + ValueDecl *EndPtrDecl = EndPtrInfo.getDecl(); + unsigned EndPtrLevel = EndPtrInfo.isDeref() ? 1 : 0; + + QualType Ty = EndPtrDecl->getType(); + assert(Ty->isSinglePointerType() || Ty->isUnspecifiedPointerType() || + Ty->isDynamicRangePointerType() || Ty->hasAttr(attr::PtrAutoAttr)); + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !Ty->isSinglePointerType()) { + Ty = S.Context.getBoundsSafetyPointerType( + Ty, BoundsSafetyPointerAttributes::single()); + } + + MakeStartedByPointerType TT(S, EndPtrLevel, *StartPtrInfo); + QualType NewTy = TT.TransformType(Ty); + EndPtrDecl->setType(NewTy); + } + } + + QualType VisitDynamicRangePointerType(const DynamicRangePointerType *T) { + if (Level == 0 && (AllowRedecl || T->getEndPointer() == nullptr)) { + // T could be a started_by() pointer type. + Expr *StartPtr = T->getStartPointer(); + auto StartPtrDecls = T->getStartPtrDecls(); + assert(StartPtr || AllowRedecl); + + assert(ConstructedType == nullptr); + // Construct an ended_by() pointer type. + QualType Ty = Visit(T->desugar()); + if (Ty.isNull()) + return QualType(); + + const auto *DRPT = cast(Ty); + assert(!DRPT->getStartPointer()); + Expr *EndPtr = DRPT->getEndPointer(); + auto EndPtrDecls = DRPT->getEndPtrDecls(); + assert(EndPtr); + if (auto OldEndPtr = T->getEndPointer()) { + assert(AllowRedecl); + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + EndPtr->Profile(NewID, S.Context, /*Canonical*/ true); + OldEndPtr->Profile(OldID, S.Context, /*Canonical*/ true); + + if (NewID != OldID) { + S.Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* end */ 3; + ConstructedType = nullptr; + return QualType(); + } + } + + // ConstructType was already set while visiting the nested PointerType. + // Reconstruct DRPT by merging started_by and ended_by. + assert(ConstructedType == DRPT); + QualType RetTy = S.Context.getDynamicRangePointerType( + DRPT->desugar(), StartPtr, EndPtr, StartPtrDecls, EndPtrDecls); + ConstructedType = RetTy->getAs(); + return RetTy; + } + return BaseClass::VisitDynamicRangePointerType(T); + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { + S.Diag(Loc, diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { + S.Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* end */ 3; + return QualType(); + } + + QualType VisitAtomicType(const AtomicType *T) { + QualType ValTy = T->getValueType(); + if (ValTy->isPointerType()) { + if (!AtomicErrorEmitted) { + S.Diag(Loc, diag::err_bounds_safety_atomic_unsupported_attribute) + << /*ended_by*/ 6; + AtomicErrorEmitted = true; + } + // Construct the atomic pointer anyway. + QualType NewValTy = Visit(ValTy); + return S.Context.getAtomicType(NewValTy); + } + return VisitType(T); + } +}; +} // namespace + +Sema::LifetimeCheckKind Sema::getLifetimeCheckKind(const VarDecl *Var) { + if (!Var) + return Sema::LifetimeCheckKind::None; + + if (Var->getStorageClass() == SC_Extern) + return Sema::LifetimeCheckKind::Extern; + if (Var->getStorageClass() == SC_PrivateExtern) + return Sema::LifetimeCheckKind::PrivateExtern; + if (Var->isLocalVarDecl()) { + if (Var->hasLocalStorage()) + return Sema::LifetimeCheckKind::NonStaticLocal; + else if (Var->isStaticLocal()) + return Sema::LifetimeCheckKind::StaticLocal; + } else if (Var->hasGlobalStorage()) { + if (Var->getStorageClass() == SC_Static) + return Sema::LifetimeCheckKind::StaticGlobal; + else + return Sema::LifetimeCheckKind::GlobalDefinition; + } + + return Sema::LifetimeCheckKind::None; +} + +namespace { + +class CollectBoundsSafetyImplicitOVEs + : public RecursiveASTVisitor { + std::set OVEs; + +public: + std::set collect(Expr *E) { + OVEs.clear(); + TraverseStmt(E); + return std::move(OVEs); + } + + bool VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *E) { + if (E->isBinding()) + OVEs.insert(E->opaquevalues_begin(), E->opaquevalues_end()); + return true; + } + + bool VisitBoundsCheckExpr(const BoundsCheckExpr *E) { + OVEs.insert(E->opaquevalues_begin(), E->opaquevalues_end()); + return true; + } +}; + +class RemoveUnusedOVEs : public TreeTransform { + const std::set &UnusedOVEs; + +public: + explicit RemoveUnusedOVEs(Sema &SemaRef, + const std::set &UnusedOVEs) + : TreeTransform(SemaRef), UnusedOVEs(UnusedOVEs) {} + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E) || UnusedOVEs.count(E)) + return TransformExpr(E->getSourceExpr()); + + assert((!E->getSourceExpr() || AlreadyTransformed(E->getType())) && + "opaque value expression requires transformation"); + return E; + } +}; + +// Collect the opaque values bound by MaterializeSequenceExpr and +// BoundsCheckExpr in OldE. Remove them from NewE if they are not bound by any +// MaterializeSequenceExpr or BoundsCheckExpr (i.e., they are unused). This is a +// workaround, because -fbounds-safety might leave some unused opaque values after +// dropping -fbounds-safety implicit casts (e.g., BoundsSafetyPointerPromotionExpr). +// Here, we don't drop every unused opaque value, but only those created by +// -fbounds-safety. +// rdar://113718929 +ExprResult removeUnusedOVEs(Sema &SemaRef, Expr *OldE, Expr *NewE) { + CollectBoundsSafetyImplicitOVEs Collector; + const auto OldOVEs = Collector.collect(OldE); + const auto NewOVEs = Collector.collect(NewE); + std::set UnusedOVEs; + std::set_difference(OldOVEs.begin(), OldOVEs.end(), NewOVEs.begin(), + NewOVEs.end(), + std::inserter(UnusedOVEs, UnusedOVEs.begin())); + if (UnusedOVEs.empty()) + return NewE; + RemoveUnusedOVEs Remover(SemaRef, UnusedOVEs); + return Remover.TransformExpr(NewE); +} + +} // namespace + +void Sema::AttachDependerDeclsAttr( + ValueDecl *NewDepender, const CountAttributedType *NewDependerCountTy, + unsigned Level) { + assert(NewDepender && NewDependerCountTy); + for (const TypeCoupledDeclRefInfo &DepDeclInfo : + NewDependerCountTy->dependent_decls()) { + Decl *Dependee = DepDeclInfo.getDecl(); + llvm::SmallVector DependerDecls; + llvm::SmallVector DependerLevels; + + if (auto *Att = Dependee->getAttr()) { + assert(Att->getIsDeref() == DepDeclInfo.isDeref()); + DependerDecls.assign(Att->dependerDecls_begin(), + Att->dependerDecls_end()); + DependerLevels.assign(Att->dependerLevels_begin(), + Att->dependerLevels_end()); + } + + DependerDecls.push_back(NewDepender); + DependerLevels.push_back(Level); + Dependee->dropAttr(); + Dependee->addAttr(DependerDeclsAttr::CreateImplicit( + Context, DepDeclInfo.isDeref(), DependerDecls.data(), + DependerDecls.size(), DependerLevels.data(), DependerLevels.size())); + } +} + +// LifetimeCheck on local variable implies ScopeCheck. +LLVM_ATTRIBUTE_UNUSED inline bool +shouldImplyScopeCheck(Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::StaticLocal || + Kind == Sema::LifetimeCheckKind::NonStaticLocal; +} + +static bool CheckArgLifetimeAndScope( + Sema &SemaRef, const ValueDecl *VD, SourceLocation ExprLoc, bool ScopeCheck, + Sema::LifetimeCheckKind LifetimeCheck, + BoundsAttributedType::BoundsAttrKind AttrKind, bool IsArray = false) { + + assert(!shouldImplyScopeCheck(LifetimeCheck) || ScopeCheck); + + bool Invalid = false; + // We need scope check when the type is initially constructed + // for local variable. + if (ScopeCheck && !SemaRef.getCurScope()->isDeclScope(VD)) { + Invalid = true; + SemaRef.Diag(ExprLoc, diag::err_bounds_safety_dynamic_bound_arg_different_scope) + << AttrKind; + } + + // Check if the argument of __counted_by/__sized_by has the same lifetime + // as the annotated variable. + if (LifetimeCheck == Sema::LifetimeCheckKind::None) + return Invalid; + + if (const auto *Arg = dyn_cast(VD)) { + Sema::LifetimeCheckKind ArgLifetimeCheck = Sema::getLifetimeCheckKind(Arg); + if (LifetimeCheck != ArgLifetimeCheck) { + auto IsGlobalDefinition = [](Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::GlobalDefinition; + }; + auto IsExtern = [](Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::Extern; + }; + + if ((IsGlobalDefinition(ArgLifetimeCheck) && IsExtern(LifetimeCheck)) || + (IsExtern(ArgLifetimeCheck) && IsGlobalDefinition(LifetimeCheck))) { + if (!IsArray) { + Invalid = true; + SemaRef.Diag(ExprLoc, diag::err_bounds_safety_global_dynamic_bound) + << AttrKind; + } else { + assert(AttrKind != BoundsAttributedType::BoundsAttrKind::EndedBy); + assert(AttrKind != + BoundsAttributedType::BoundsAttrKind::CountedByOrNull); + assert(AttrKind != + BoundsAttributedType::BoundsAttrKind::SizedByOrNull); + SemaRef.Diag(ExprLoc, diag::warn_bounds_safety_extern_array_dynamic_count) + << AttrKind; + } + } else { + Invalid = true; + SemaRef.Diag(ExprLoc, + diag::err_bounds_safety_dynamic_bound_arg_different_lifetime) + << AttrKind; + } + } + } + return Invalid; +} + +static bool diagnoseBoundsAttrLifetimeAndScope( + Sema &SemaRef, const BoundsAttributedType *Ty, bool ScopeCheck, + Sema::LifetimeCheckKind LifetimeCheck) { + assert(Ty); + bool HadError = false; + if (const auto *CAT = dyn_cast(Ty)) { + for (const TypeCoupledDeclRefInfo &DepDeclInfo : CAT->dependent_decls()) { + const auto *Dependee = cast(DepDeclInfo.getDecl()); + // Variables marked as const or __unsafe_late_const are the exceptions + // that are allowed to be referred to by bounds annotations on a struct + // field or a function parameter. Thereby, skip diagnosis here. + if (clang::IsConstOrLateConst(Dependee)) + continue; + + HadError |= CheckArgLifetimeAndScope( + SemaRef, Dependee, CAT->getCountExpr()->getExprLoc(), ScopeCheck, + LifetimeCheck, CAT->getKind(), CAT->isArrayType()); + } + } else if (const auto *RAT = dyn_cast(Ty)) { + for (const TypeCoupledDeclRefInfo &EndPtrInfo : RAT->endptr_decls()) { + const auto *EndPtr = cast(EndPtrInfo.getDecl()); + if (clang::IsConstOrLateConst(EndPtr)) + continue; + + HadError |= CheckArgLifetimeAndScope( + SemaRef, EndPtr, RAT->getEndPointer()->getExprLoc(), ScopeCheck, + LifetimeCheck, RAT->getKind()); + } + } else { + llvm_unreachable("Unexpected bounds attribute kind"); + } + return HadError; +} + +unsigned TransitiveFieldCopyCount(const RecordDecl *RD, + const FieldDecl *Inner) { + auto DeclCtx = Inner->getDeclContext(); + if (DeclCtx == RD) + return 1; + unsigned Count = 0; + for (auto FD : RD->fields()) { + if (auto InnerStructTy = FD->getType()->getAs()) { + Count += TransitiveFieldCopyCount( + cast(InnerStructTy->getDecl()), Inner); + } + } + return Count; +} + +/// Diagnose compatibility between a declaration whose type is annotated with +/// \c counted_by or \c sized_by and the declarations referred to by the annotation. +/// +/// \param TheDepender Decl whose type contains a \c counted_by or \c sized_by +/// attribute. +/// +/// \param Level Indicates the nested level of \p PointerTy in \c +/// TheDepender->getType() . +/// +/// \param IsFPtr The attribute is added to a function pointer type. +/// +/// \returns \c true if the function emitted an error. +static bool diagnoseCountDependentDecls(Sema &S, const ValueDecl *TheDepender, + const CountAttributedType *PointerTy, + unsigned Level, bool IsFPtr) { + assert(TheDepender && PointerTy); + bool HadError = false; + for (const TypeCoupledDeclRefInfo &DepDeclInfo : + PointerTy->dependent_decls()) { + Decl *Dependee = DepDeclInfo.getDecl(); + // Variables marked as const or __unsafe_late_const are the exceptions that + // are allowed to be referred to by bounds annotations on a struct field + // or a function parameter. Thereby, skip diagnosis here. + if (Level == 0 && clang::IsConstOrLateConst(Dependee)) + continue; + + if (auto *Att = Dependee->getAttr()) { + if (!Att->isInherited() || isa(Dependee)) { + bool HadSharedDeclError = false; + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const auto *OtherDepender = + cast(Att->dependerDecls_begin()[i]); + if (TheDepender != OtherDepender) { + QualType OtherDependerTy = OtherDepender->getType(); + + // Local variables are not allowed to share the same count variable. + const auto *DependeeVD = dyn_cast(Dependee); + if (DependeeVD && DependeeVD->isLocalVarDecl()) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_external_bound_share) + << DependeeVD << /*variable*/ 0 << PointerTy->getKind(); + + if (unsigned DependerLevel = Att->dependerLevels_begin()[i]) { + assert(DependerLevel == 1); + OtherDependerTy = OtherDependerTy->getPointeeType(); + } + + const auto *DependerDCPT = + OtherDependerTy->getAs(); + assert(DependerDCPT); + S.Diag(DependerDCPT->getCountExpr()->getExprLoc(), + diag::note_previous_use); + HadError = HadSharedDeclError = true; + break; + } + + // Flexible array members are not allowed to share count with other + // fields + if (OtherDependerTy->isArrayType() && PointerTy->isArrayType()) { + if (auto FD = dyn_cast(TheDepender)) { + const RecordDecl *RD = FD->getParent(); + // FAM cannot share length field with other __counted_by. + // This filters out the case where FAMs in different struct + // types share a length field in a common nested struct. I.e. + // struct A and B both contain struct C. The FAMs in A and B + // both refer to the length field in C. The two FAMs do not + // exist in the same struct so the length isn't truly shared. In + // this case RD may be A, and OtherDepender may be the FAM + // in B. + if (RD->isParentStructOf(OtherDepender)) { + S.Diag(TheDepender->getLocation(), + diag::err_bounds_safety_external_bound_share) + << cast(Dependee) << /*field*/ 1 + << PointerTy->getKind(); + if (auto DependerLevel = Att->dependerLevels_begin()[i]) { + if (DependerLevel == 1) + OtherDependerTy = OtherDependerTy->getPointeeType(); + } + const auto *DependerDCPT = + OtherDependerTy->getAs(); + const auto *CountExpr = DependerDCPT->getCountExpr(); + S.Diag(CountExpr->getExprLoc(), diag::note_previous_use) + << CountExpr->getSourceRange(); + HadError = HadSharedDeclError = true; + break; + } + } + } + } + } + if (HadSharedDeclError) + continue; + } + } + if (auto FD = dyn_cast(TheDepender)) { + const RecordDecl *RD = FD->getParent(); + if (auto FD = dyn_cast(Dependee)) { + if (TransitiveFieldCopyCount(RD, FD) > 1) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_dependent_field_duplicates) + << FD << RD; + HadError = true; + continue; + } + } + } + if (!IsFPtr && isa(TheDepender) && !isa(Dependee)) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 0 /*Field*/; + HadError = true; + continue; + } + if ((IsFPtr || isa(TheDepender)) && + !isa(Dependee)) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 1 /*Param*/; + HadError = true; + continue; + } + } + return HadError; +} + +/// Diagnose compatibility between a declaration whose type is annotated with +/// \c ended_by and the declarations referred to by the annotation. +/// +/// \param TheDepender Decl whose type contains a \c ended_by attribute. +/// +/// \param Level Indicates the nested level of \p PointerTy in \c +/// TheDepender->getType() . +/// +/// \param IsFPtr The attribute is added to a function pointer type. +/// +/// \returns \c true if the function emitted an error. +static bool +diagnoseRangeDependentDecls(Sema &S, const ValueDecl *TheDepender, + const DynamicRangePointerType *PointerTy, + unsigned Level, bool IsFPtr) { + assert(TheDepender && PointerTy); + bool HadError = false; + for (const TypeCoupledDeclRefInfo &EndPtrInfo : PointerTy->endptr_decls()) { + // Variables marked as const or __unsafe_late_const are the exceptions that + // are allowed to be referred to by bounds annotations on a struct field + // or a function parameter. Thereby, skip diagnosis here. + if (Level == 0 && clang::IsConstOrLateConst(EndPtrInfo.getDecl())) + continue; + + if (!IsFPtr && isa(TheDepender) && + !isa(EndPtrInfo.getDecl())) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 0; + HadError = true; + continue; + } + if ((IsFPtr || isa(TheDepender)) && + !isa(EndPtrInfo.getDecl())) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 1; + HadError = true; + continue; + } + QualType EndPtrTy = EndPtrInfo.getDecl()->getType(); + if (EndPtrInfo.isDeref()) { + EndPtrTy = EndPtrTy->getPointeeType(); + } + if (auto *EndDRPT = EndPtrTy->getAs()) { + for (auto StartPtrInfo : EndDRPT->startptr_decls()) { + if (StartPtrInfo.getDecl() != TheDepender) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_external_bound_share) + << EndPtrInfo.getDecl() << /*variable*/ 0 << /* ended_by */ 4; + QualType PrevUserTy = StartPtrInfo.getDecl()->getType(); + + if (StartPtrInfo.isDeref()) { + PrevUserTy = PrevUserTy->getPointeeType(); + } + auto *PrevUserDRPT = PrevUserTy->getAs(); + assert(PrevUserDRPT && PrevUserDRPT->getEndPointer()); + S.Diag(PrevUserDRPT->getEndPointer()->getExprLoc(), + diag::note_previous_use); + HadError = true; + continue; + } + } + } + } + return HadError; +} + +void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, + AttributeCommonInfo::Kind Kind, + Expr *AttrArg, SourceLocation Loc, + SourceRange Range, StringRef DiagName, + bool OriginatesInAPINotes) { + // If the decl is invalid, the indirection Level might not exist in the type, + // since the type may have not been constructed correctly. Example: + // 'int (*param)[__counted_by_or_null(10)][]' + // Since you cannot create an array of unsized arrays, the DeclTy ends + // up being 'int *'. The attribute's Level is 1, but we only have level 0. + if (D->isInvalidDecl()) + return; + + auto *TND = dyn_cast(D); + auto *VD = dyn_cast(D); + auto *Var = dyn_cast(D); + QualType DeclTy = TND ? TND->getUnderlyingType() : VD->getType(); + + bool IsFPtr = false; + unsigned EffectiveLevel = Level; + QualType Ty = DeclTy; + for (unsigned i = 0; i != Level; ++i) { + if (!Ty->isPointerType()) + break; + Ty = Ty->getPointeeType(); + if (Ty->isFunctionType()) { + IsFPtr = true; + EffectiveLevel = Level - i - 1; + break; + } + } + + // Don't allow typedefs with __counted_by on non-function types. + if (TND && (!DeclTy->isFunctionType() && !IsFPtr)) { + Diag(Loc, diag::err_bounds_safety_typedef_dynamic_bound) << DiagName; + return; + } + + bool CountInBytes = false; + bool IsEndedBy = false; + bool OrNull = false; + switch (Kind) { + case ParsedAttr::AT_SizedBy: + CountInBytes = true; + break; + case ParsedAttr::AT_SizedByOrNull: + CountInBytes = true; + OrNull = true; + break; + case ParsedAttr::AT_CountedBy: + break; + case ParsedAttr::AT_CountedByOrNull: + OrNull = true; + break; + case ParsedAttr::AT_PtrEndedBy: + IsEndedBy = true; + break; + default: + llvm_unreachable("Invalid dynamic bound attribute"); + } + + if (!IsEndedBy) { + // Nullability as indicated by _Nonnull or _Nullable. Does not impact + // semantics, only warnings. + std::optional AttrNullability = DeclTy->getNullability(); + if (OrNull) { + // Function parameter/return value attribute that *does* impact semantics, + // letting the compiler elide null checks. This could remove bounds safety + // checks, so using it together with __counted_by_or_null is not safe. + // The nonnull attribute does not apply to return values, so ignore that + // if used as a function attribute: in that case the parameters will be + // checked in attrNonNullArgCheck & handleNonNullAttr. + if (auto NNAttr = D->getAttr(); + NNAttr && isa(D)) { + Diag(Loc, diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << NNAttr << Range << NNAttr->getRange(); + return; + } + if (auto RNNAttr = D->getAttr()) { + Diag(Loc, diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << RNNAttr << Range << RNNAttr->getRange(); + return; + } + + if (AttrNullability == NullabilityKind::NonNull) { + Diag(Loc, diag::warn_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << DiagName; + } + } + + if (auto CountArg = AttrArg->getIntegerConstantExpr(Context)) { + if (CountArg > 0 && !OrNull && + AttrNullability == NullabilityKind::Nullable) + Diag(AttrArg->getExprLoc(), + diag::warn_bounds_safety_nonnullable_dynamic_count_nullable) + << CountInBytes << Range; + } + } + + if (VD) { + const auto *FD = dyn_cast(VD); + if (FD && FD->getParent()->isUnion()) { + Diag(Loc, diag::err_invalid_decl_kind_bounds_safety_union_count) + << DiagName; + return; + } + + if (EffectiveLevel != 0 && + (!isa(VD) || DeclTy->isBoundsAttributedType())) { + Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName; + return; + } + + // Clang causes array parameters to decay to pointers so quickly that + // attributes aren't even parsed yet. This causes arrays with both an + // explicit size and a count attribute to go to the CountAttributedType + // case of ConstructCountAttributedType, which complains that the type + // has two count attributes. See if we can produce a better diagnostic here + // instead. + if (const auto *PVD = dyn_cast_or_null(Var)) { + QualType TSITy = PVD->getTypeSourceInfo()->getType(); + if (IsEndedBy) { + if (Level == 0 && TSITy->isArrayType()) { + Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0; + return; + } + } else { + const auto *ATy = Context.getAsArrayType(TSITy); + if (Level == 0 && ATy && !ATy->isIncompleteArrayType() && + !TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + Diag(Loc, diag::err_bounds_safety_complete_array_with_count); + return; + } + } + } + + if (Ty->isArrayType() && OrNull && + (FD || EffectiveLevel > 0 || (Var && Var->hasExternalStorage()))) { + auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam); + // Pointers to dynamic count types are only allowed for parameters, so any + // FieldDecl containing a dynamic count type is a FAM. I.e. a struct field + // with type 'int(*)[__counted_by(...)]' is not valid. + ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName; + assert(!FD || EffectiveLevel == 0); + + SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc); + SourceLocation EndLoc = + Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1, + getSourceManager(), getLangOpts()); + std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by"; + ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute); + + return; + } + + if (Ty->isArrayType() && EffectiveLevel > 0) { + auto ErrDiag = + Diag( + Loc, + diag:: + err_bounds_safety_unsupported_address_of_incomplete_array_type) + << Ty; + // apply attribute anyways to avoid too misleading follow-up diagnostics + } + } + + QualType NewDeclTy{}; + bool ScopeCheck = (Var && Var->isLocalVarDecl()) || IsFPtr; + const BoundsAttributedType *ConstructedType = nullptr; + + bool HadAtomicError = false; + if (IsEndedBy) { + if (AttrArg && AttrArg->getType()->isAtomicType()) { + Diag(Loc, diag::err_bounds_safety_atomic_unsupported_attribute) + << /*started_by*/ 8; + return; + } + if (!AttrArg || !AttrArg->getType()->isPointerType()) { + Diag(Loc, diag::err_attribute_argument_type_for_bounds_safety_range) + << DiagName; + return; + } + + // Make started_by() pointers if VD is a field or variable. We don't want to + // create started_by(X) pointers where X is a function etc. + std::optional StartPtrInfo; + if (VD && (isa(VD) || isa(VD))) { + assert(Level <= 1); + StartPtrInfo = TypeCoupledDeclRefInfo(VD, /*Deref=*/Level != 0); + } + + auto TypeConstructor = ConstructDynamicRangePointerType( + *this, Level, DiagName, AttrArg, Loc, OriginatesInAPINotes, ScopeCheck, + StartPtrInfo); + NewDeclTy = TypeConstructor.Visit(DeclTy); + HadAtomicError = TypeConstructor.hadAtomicError(); + ConstructedType = TypeConstructor.getConstructedType(); + } else { + auto TypeConstructor = ConstructCountAttributedType( + *this, Level, DiagName, AttrArg, Loc, CountInBytes, OrNull, + OriginatesInAPINotes, ScopeCheck); + NewDeclTy = TypeConstructor.Visit(DeclTy); + HadAtomicError = TypeConstructor.hadAtomicError(); + ConstructedType = TypeConstructor.getConstructedType(); + } + + if (NewDeclTy.isNull()) + return; + + // `NewDeclTy` is the new type of the declaration `D`, whereas + // `ConstructedType` is exact `BoundsAttributedType` that's created for this + // attribute. The bounds attribute can be added to a nested pointer or some + // nested type, so `ConstructedType` can be different from `NewDeclTy`. + assert(ConstructedType); + + auto LifetimeCheck = + IsFPtr ? Sema::LifetimeCheckKind::None : Sema::getLifetimeCheckKind(Var); + if (diagnoseBoundsAttrLifetimeAndScope(*this, ConstructedType, ScopeCheck, + LifetimeCheck)) + return; + + if (VD && !isa(VD) && !HadAtomicError) { + if (const auto *BDTy = dyn_cast(ConstructedType)) { + if (!diagnoseCountDependentDecls(*this, VD, BDTy, EffectiveLevel, IsFPtr)) + AttachDependerDeclsAttr(VD, BDTy, EffectiveLevel); + } else if (const auto *BDTy = + dyn_cast(ConstructedType)) { + diagnoseRangeDependentDecls(*this, VD, BDTy, EffectiveLevel, IsFPtr); + } else { + llvm_unreachable("Unexpected bounds attributed type"); + } + } + + if (TND) { + // We need to create a TypeSourceInfo, as TypedefNameDecl is not a ValueDecl + // and thus doesn't have setType() method. + SourceLocation Loc = TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + TypeSourceInfo *Info = Context.getTrivialTypeSourceInfo(NewDeclTy, Loc); + TND->setTypeSourceInfo(Info); + } else { + VD->setType(NewDeclTy); + // Reconstruct implicit cast for initializer after variable type change. + if (Var && Var->hasInit()) { + Expr *Init = Var->getInit(); + ExprResult Res = removeUnusedOVEs(*this, Init, Init->IgnoreImpCasts()); + if (Res.isInvalid()) + return; + AddInitializerToDecl(Var, Res.get(), Var->isDirectInit()); + } + } +} + +static void handlePtrCountedByEndedByAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + unsigned Level; + if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(1), Level)) + return; + AttributeCommonInfo::Kind Kind = AL.getKind(); + const IdentifierInfo *AttrName = + AL.printAsMacro() ? AL.getMacroIdentifier() : AL.getAttrName(); + S.applyPtrCountedByEndedByAttr(D, Level, Kind, AL.getArgAsExpr(0), + AL.getLoc(), AL.getRange(), + ("\'" + AttrName->getName() + "\'").str()); +} + +static void handleUnsafeLateConst(Sema &S, Decl *D, + const ParsedAttr &AL) { + D->addAttr(::new (S.Context) UnsafeLateConstAttr(S.Context, AL)); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.Context.getTargetInfo().getTriple().isOSAIX()) { @@ -5975,7 +7267,19 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { auto *FD = dyn_cast(D); - assert(FD); + /* TO_UPSTREAM(BoundsSafety) ON */ + // In upstream 'counted_by' is subjected to FieldDecl only, whereas internally, + // the attribute applies to any other subjects. This code is to manually handle + // cases when the counted_by is applied to non-field decl without + // -fbounds-safety. + if (!isa(D)) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_decl_type_str) + << AL.getAttrName()->getName() << AL.isRegularKeywordAttribute() + << "non-static data members"; + AL.setInvalid(); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ auto *CountExpr = AL.getArgAsExpr(0); if (!CountExpr) @@ -6975,9 +8279,25 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_CountedByOrNull: case ParsedAttr::AT_SizedBy: case ParsedAttr::AT_SizedByOrNull: + /* TO_UPSTREAM(BoundsSafety) ON */ + if (S.getLangOpts().BoundsSafetyAttributes) { + handlePtrCountedByEndedByAttr(S, D, AL); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ handleCountedByAttrField(S, D, AL); break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety attributes: + case ParsedAttr::AT_PtrEndedBy: + handlePtrCountedByEndedByAttr(S, D, AL); + break; + case ParsedAttr::AT_UnsafeLateConst: + handleUnsafeLateConst(S, D, AL); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Microsoft attributes: case ParsedAttr::AT_LayoutVersion: handleLayoutVersion(S, D, AL); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 427ffd9061ef3..bbebd48110b83 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1277,6 +1277,15 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::StmtExprClass: case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::AssumptionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: case Expr::CXXParenListInitExprClass: return canSubStmtsThrow(*this, S); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index c27bf0fb89d25..a881781fa56fe 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" #include "CheckExprLifetime.h" #include "TreeTransform.h" #include "UsedDeclVisitor.h" @@ -30,9 +31,11 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" @@ -507,8 +510,245 @@ SourceRange Sema::getExprRange(Expr *E) const { // Standard Promotions and Conversions //===----------------------------------------------------------------------===// +/*TO_UPSTREAM(BoundsSafety) ON*/ +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast, + BoundsSafetyPointerAttributes FA) { + SourceLocation Loc = PointerToCast->getBeginLoc(); + auto SQT = PointerToCast->getType()->getPointeeType().split(); + SQT.Ty = S.Context.CharTy.getTypePtr(); + QualType QualifiedChar = S.Context.getQualifiedType(SQT); + QualType CharPtrTy = S.Context.getPointerType(QualifiedChar, FA); + return S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(CharPtrTy), Loc, PointerToCast); +} + +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast) { + return CastToCharPointer(S, PointerToCast, + BoundsSafetyPointerAttributes::bidiIndexable()); +} + +static bool ShouldAssumeSGTZero(Sema &S, Expr *ValueToTest) { + QualType CountT = ValueToTest->getType(); + if (!CountT->isUnsignedIntegerOrEnumerationType()) + return false; + return S.Context.getTypeSize(CountT) >= S.Context.getTypeSize( + S.Context.getSignedSizeType()); +} + +static ExprResult AssumeSGTZero(Sema &S, Expr *ValueToTest, Expr *ValueToWrap) { + // If the count is unsigned and the same size as size_t, assume that it is + // signed-positive. + QualType CountT = ValueToTest->getType(); + CountT = S.Context.getCorrespondingSignedType(CountT); + ExprResult Cast = S.ImpCastExprToType( + ValueToTest, CountT, CK_IntegralCast); + if (!(ValueToTest = Cast.get())) + return ExprError(); + ExprResult Zero = S.ActOnIntegerConstant(ValueToTest->getBeginLoc(), 0); + ExprResult Comparison = S.CreateBuiltinBinOp( + ValueToTest->getBeginLoc(), BO_GE, ValueToTest, Zero.get()); + if (!Comparison.get()) + return ExprError(); + return AssumptionExpr::Create( + S.Context, ValueToWrap, Comparison.get()); +} + +static ExprResult PromoteBoundsSafetyPointerWithCount(Sema &S, + OpaqueValueExpr *BasePtr, + OpaqueValueExpr *Count, + bool IsCountInBytes, + bool NullCheck) { + QualType T = BasePtr->getType(); + + ExprResult PtrVal = BasePtr; + ExprResult Upper = BasePtr; + + auto TFA = T->getAs()->getPointerAttributes(); + if (!TFA.isUnsafeOrUnspecified()) { + T = S.Context.getPointerType( + T->getPointeeType(), BoundsSafetyPointerAttributes::unspecified()); + Upper = ImplicitCastExpr::Create( + S.Context, T, CK_BoundsSafetyPointerCast, Upper.get(), nullptr, + VK_PRValue, S.CurFPFeatureOverrides()); + } + + bool CastToChar = + IsCountInBytes && S.Context.getTypeSizeInCharsIfKnown(T->getPointeeType()) + .value_or(CharUnits::Zero()) + .getQuantity() != 1; + if (CastToChar) { + Upper = CastToCharPointer(S, Upper.get(), + T->getAs()->getPointerAttributes()); + if (!Upper.get()) + return ExprError(); + } + + Expr *CountE = Count; + if (ShouldAssumeSGTZero(S, CountE)) { + ExprResult Assumed = AssumeSGTZero(S, CountE, CountE); + if (!(CountE = Assumed.get())) + return ExprError(); + } + Upper = S.CreateBuiltinBinOp( + Upper.get()->getBeginLoc(), BO_Add, Upper.get(), CountE); + if (!Upper.get()) + return ExprError(); + + if (CastToChar) { + Upper = ImplicitCastExpr::Create( + S.Context, T, CK_BitCast, Upper.get(), nullptr, VK_PRValue, + S.CurFPFeatureOverrides()); + } + + QualType FPtrTy = S.Context.getPointerType( + T->getPointeeType(), BoundsSafetyPointerAttributes::bidiIndexable()); + return BoundsSafetyPointerPromotionExpr::Create(S.Context, FPtrTy, PtrVal.get(), + Upper.get(), nullptr, NullCheck); +} + +static ExprResult +PromoteBoundsSafetyPointerToFlexibleArrayMember(Sema &S, RecordDecl *RD, Expr *E, + bool NullCheck = true) { + FlexibleArrayMemberUtils FlexUtils(S); + SmallVector PathToFlex; + ArrayRef CountDecls; + if (!FlexUtils.Find(RD, PathToFlex, CountDecls)) { + // cannot promote; return original expression. this will be diagnosed + // elsewhere + return E; + } + + SmallVector OVEs; + + auto *OE = OpaqueValueExpr::EnsureWrapped(S.Context, E, OVEs); + Expr *FlexibleObj = FlexUtils.SelectFlexibleObject(PathToFlex, OE); + ExprResult CountExpr = FlexUtils.BuildCountExpr( + PathToFlex.back(), CountDecls, FlexibleObj, OVEs); + if (!CountExpr.get()) + return ExprError(); + + CopyExpr Copier(S); + Expr *Count = CountExpr.get(); + if (ShouldAssumeSGTZero(S, Count)) { + Expr *ClonedCount = S.DefaultLvalueConversion(Copier.TransformExpr(Count).get()).get(); + ExprResult Assumed = + AssumeSGTZero(S, ClonedCount, Count); + if (!(Count = Assumed.get())) + return ExprError(); + } + + // build end address + QualType FlexType = PathToFlex.back()->getType(); + Expr *AnyStructBase = FlexibleObj; + if (AnyStructBase->getType()->isPointerType() && !AnyStructBase->isPRValue()) + AnyStructBase = ImplicitCastExpr::Create( + S.Context, AnyStructBase->getType(), CK_LValueToRValue, + AnyStructBase, nullptr, VK_PRValue, S.CurFPFeatureOverrides()); + Expr *ObjEnd = MemberExpr::CreateImplicit( + S.Context, AnyStructBase, /*IsArrow*/ AnyStructBase->getType()->isPointerType(), + PathToFlex.back(), FlexType, VK_LValue, OK_Ordinary); + // Ensure that the pointer isn't __bidi_indexable, as that would be + // problematic; we're currently trying to compute what the bounds of that + // pointer should be. + QualType DecayedTy = S.Context.getArrayDecayedType(FlexType); + DecayedTy = S.Context.getBoundsSafetyPointerType(DecayedTy, {}); + ExprResult Res = S.ImpCastExprToType( + ObjEnd, DecayedTy, CK_ArrayToPointerDecay, VK_PRValue); + if (!(ObjEnd = Res.get())) + return ExprError(); + + // ArrayToPointerDecay overrides the pointer attributes to be bidi_indexable, + // force it to be unspecified. + ObjEnd->setType(DecayedTy); + + Res = S.CreateBuiltinBinOp(E->getBeginLoc(), BO_Add, ObjEnd, Count); + if (!(ObjEnd = Res.get())) + return ExprError(); + + // build FPPE and materializing expressions around it + auto Bidi = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType FPtrTy = S.Context.getPointerType( + E->getType()->getPointeeType(), Bidi); + + // Promoting a pointer to struct with flexible array member to a wide + // pointer requires implicit count member access to the pointer. If the + // pointer is null, this will cause an uninteded null pointer dereference. + // To avoid this, we skip the evaluation of the bounds of the promotion + // expression if the base pointer is null. We control this by adding a + // 'NullCheck' member to `BoundsSafetyPointerPromotionExpr`. This is effectively + // same as adding this conditional operator (`OE ? Result : 0`). However, we + // don't add such an expression in the AST because the conditional operator + // will interfere our CFG analysis for dynamic count assignments. Also, we + // don't have a good way to remove the conditional operator along with the + // functionality to ignore implicit casts. A materialization expr wraps the + // promotion with null check and if the materialization expr materializes an + // OVE containing member access first, it will still introduce an unintended + // null pointer dereference. We prevent this by not wrapping the count member + // access in OVE but ensuring it's rebuilt for every reuse. + Expr *Result = BoundsSafetyPointerPromotionExpr::Create( + S.Context, FPtrTy, OE, ObjEnd, /*LowerBound*/ nullptr, NullCheck); + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); + } + return Result; +} + +static ExprResult +PromoteBoundsSafetyFlexibleArrayMember(Sema &S, MemberExpr *M, Expr *ArrayBase) { + SmallVector OVEs; + Expr *BasePtr = OpaqueValueExpr::EnsureWrapped(S.Context, M->getBase(), OVEs); + M->setBase(BasePtr); + + if (auto *PT = BasePtr->getType()->getAs()) { + if (!PT->getPointerAttributes().hasUpperBound()) { + auto *RecordPointee = PT->getPointeeType()->getAs(); + auto *RD = RecordPointee->getDecl(); + assert(RD->hasFlexibleArrayMember() && + RD->getTagKind() != TagTypeKind::Union); + // Skipping the null check for the struct base because it must have been + // explicitly dereferenced (e.g., base->array) to get here. + ExprResult Promoted = PromoteBoundsSafetyPointerToFlexibleArrayMember( + S, RD, BasePtr, /*NullCheck*/ false); + if (!(BasePtr = Promoted.get())) + return ExprError(); + } + } else { + assert(BasePtr->isLValue()); + // Using '&' on an object with a flexible array member will give us a + // properly promoted pointer to it. + ExprResult AddrOf = S.CreateBuiltinUnaryOp( + BasePtr->getBeginLoc(), UO_AddrOf, BasePtr); + if (!(BasePtr = AddrOf.get())) + return ExprError(); + } + + ExprResult Upper = S.BuildUpperBoundExpr( + BasePtr, ArrayBase->getBeginLoc(), SourceLocation()); + if (!Upper.get()) + return ExprError(); + + QualType FPtrTy = S.Context.getBoundsSafetyPointerType( + ArrayBase->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + Expr *Result = BoundsSafetyPointerPromotionExpr::Create( + S.Context, FPtrTy, ArrayBase, Upper.get()); + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); + } + return Result; +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + /// DefaultFunctionArrayConversion (C99 6.3.2.1p3, C99 6.3.2.1p4). -ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { +ExprResult Sema::DefaultFunctionArrayConversion( + Expr *E, bool Diagnose, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion, + bool DisableFlexibleArrayPromotion) { + /* TO_UPSTREAM(BoundsSafety) OFF */ // Handle any placeholder expressions which made it here. if (E->hasPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -525,7 +765,12 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { if (!checkAddressOfFunctionIsAvailable(FD, Diagnose, E->getExprLoc())) return ExprError(); - E = ImpCastExprToType(E, Context.getPointerType(Ty), + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyPointerAttributes FA; + if (getLangOpts().BoundsSafety) + FA.setSingle(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + E = ImpCastExprToType(E, Context.getPointerType(Ty, FA), CK_FunctionToPointerDecay).get(); } else if (Ty->isArrayType()) { // In C90 mode, arrays only promote to pointers if the array expression is @@ -541,9 +786,28 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { // if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) { ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty), - CK_ArrayToPointerDecay); + CK_ArrayToPointerDecay, VK_PRValue, nullptr, + CheckedConversionKind::Implicit, + // TO_UPSTREAM(BoundsSafety) + DiagnoseBoundsSafetyIncompleteArrayPromotion); if (Res.isInvalid()) return ExprError(); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: if decaying a member access and that member has a flexible + // array member, we must wrap it in the appropriate promotion legalese. + if (getLangOpts().BoundsSafety && !DisableFlexibleArrayPromotion) { + if (Ty->isIncompleteArrayType() && Ty->isCountAttributedType()) { + if (auto *Member = dyn_cast(E->IgnoreParens())) { + Res.get()->setType(Context.getArrayDecayedType(Ty)); + Res = + PromoteBoundsSafetyFlexibleArrayMember(*this, Member, Res.get()); + if (Res.isInvalid()) + return ExprError(); + } + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ E = Res.get(); } } @@ -632,7 +896,25 @@ static void DiagnoseDirectIsaAccess(Sema &S, const ObjCIvarRefExpr *OIRE, } } -ExprResult Sema::DefaultLvalueConversion(Expr *E) { +/*TO_UPSTREAM(BoundsSafety) ON*/ +static RecordDecl *getImmediateDeclForFlexibleArrayPromotion(QualType T) { + if (T->isSinglePointerType() && !T->isBoundsAttributedType()) { + auto *PT = T->getAs(); + if (auto *RecordPointee = PT->getPointeeType()->getAs()) { + auto *RD = RecordPointee->getDecl(); + if (RD->hasFlexibleArrayMember() && + RD->getTagKind() != TagTypeKind::Union) { + return RD; + } + } + } + return nullptr; +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + +ExprResult Sema::DefaultLvalueConversion(Expr *E, + // TO_UPSTREAM(BoundsSafety) + bool DisableFlexibleArrayPromotion) { // Handle any placeholder expressions which made it here. if (E->hasPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -726,6 +1008,132 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) Cleanup.setExprNeedsCleanups(true); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: rvalue of pointers with dynamic count or range will + // automatically form wide pointers. + auto *DBPT = T->getAs(); + if (getLangOpts().BoundsSafety && DBPT) { + CopyExpr Copy(*this); + SmallVector OpaqueValues; + // if the lvalue was a member access, it's possible its base expression + // has side effects, in which case we need to wrap it in an opaque value + // expression. + auto *UnwrappedExp = E; + bool IsMemberOVE = false; + if (const auto *MemberOVE = dyn_cast(UnwrappedExp)) { + UnwrappedExp = MemberOVE->getSourceExpr(); + IsMemberOVE = true; + } + auto *Member = dyn_cast(UnwrappedExp->IgnoreParenCasts()); + if (DBPT->referencesFieldDecls() && Member) { + auto *MemberBase = Member->getBase(); + if (!isa(MemberBase)) { + // When a MemberExpr is materialized, the base should also be. + (void)IsMemberOVE; + assert(!IsMemberOVE); + MemberBase = OpaqueValueExpr::EnsureWrapped( + Context, MemberBase, OpaqueValues); + // Rebuild the member expr with the new base so that when + // the caller of LvalueConversion throw away the result, + // the OVE also goes away. An example is 'rewriteBuiltinFunctionDecl()' + // which calls LvalueConversion just to check the resulting type. + Res = MemberExpr::Create( + Context, MemberBase, Member->isArrow(), Member->getOperatorLoc(), + Member->getQualifierLoc(), Member->getTemplateKeywordLoc(), + Member->getMemberDecl(), Member->getFoundDecl(), + Member->getMemberNameInfo(), /*CopiedTemplateArgs(Member)*/ nullptr, + Member->getType(), Member->getValueKind(), Member->getObjectKind(), + Member->isNonOdrUse()); + } + for (const auto &DeclRefInfo : DBPT->dependent_decls()) { + auto *Decl = DeclRefInfo.getDecl(); + ExprObjectKind OK = OK_Ordinary; + if (auto *FD = dyn_cast(Decl)) { + OK = FD->isBitField() ? OK_BitField : OK_Ordinary; + } + auto *NewMember = MemberExpr::CreateImplicit( + Context, MemberBase, Member->isArrow(), Decl, Decl->getType(), + VK_LValue, OK); + ExprResult Lvalue = ImplicitCastExpr::Create( + Context, NewMember->getType(), CK_LValueToRValue, NewMember, + nullptr, VK_PRValue, CurFPFeatureOverrides()); + if (!Lvalue.get()) + return ExprError(); + auto *OVE = OpaqueValueExpr::EnsureWrapped( + Context, Lvalue.get(), OpaqueValues); + Copy.AddDeclSubstitution(Decl, OVE); + } + } + + CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; + Res = ImplicitCastExpr::Create(Context, T, CK, Res.get(), nullptr, + VK_PRValue, CurFPFeatureOverrides()); + + if (auto *DCPT = dyn_cast(DBPT)) { + ExprResult Count = Copy.TransformExpr(DCPT->getCountExpr()); + if (!Count.get()) + return ExprError(); + Count = DefaultLvalueConversion(Count.get()); + if (!Count.get()) + return ExprError(); + + if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get())) + return ExprError(); + + // Need to transform the base value so that + // PromoteBoundsSafetyPointerWithCount can reuse it with impunity + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, Res.get(), OpaqueValues); + auto *CountOVE = OpaqueValueExpr::EnsureWrapped( + Context, Count.get(), OpaqueValues); + Res = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, CountOVE, DCPT->isCountInBytes(), DCPT->isOrNull()); + if (!Res.get()) + return ExprError(); + } else { + auto *DRPT = cast(DBPT); + // XXX: we would prefer to call DefaultLvalueConversion for Lower and + // Upper here, but sadly this would infinitely recurse. + ExprResult Lower; + if (auto *StartExpr = DRPT->getStartPointer()) + Lower = Copy.TransformExpr(StartExpr); + else + Lower = Copy.TransformExpr(E); + if (!Lower.get()) + return ExprError(); + + ExprResult Upper; + if (auto *EndExpr = DRPT->getEndPointer()) + Upper = Copy.TransformExpr(EndExpr); + else + Upper = Copy.TransformExpr(E); + if (!Upper.get()) + return ExprError(); + + if (Lower.get()->isGLValue()) + Lower = ImplicitCastExpr::Create( + Context, Lower.get()->getType(), CK_LValueToRValue, Lower.get(), + nullptr, VK_PRValue, CurFPFeatureOverrides()); + if (Upper.get()->isGLValue()) + Upper = ImplicitCastExpr::Create( + Context, Upper.get()->getType(), CK_LValueToRValue, Upper.get(), + nullptr, VK_PRValue, CurFPFeatureOverrides()); + + BoundsSafetyPointerAttributes AT; + AT.setBidiIndexable(); + QualType FPtrTy = Context.getPointerType(T->getPointeeType(), AT); + Res = BoundsSafetyPointerPromotionExpr::Create( + Context, FPtrTy, E, Upper.get(), Lower.get()); + } + + if (!OpaqueValues.empty()) { + Res = MaterializeSequenceExpr::Create(Context, Res.get(), OpaqueValues); + Res = MaterializeSequenceExpr::Create(Context, Res.get(), OpaqueValues, true); + } + return Res; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; @@ -741,14 +1149,37 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { nullptr, VK_PRValue, FPOptionsOverride()); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Promote a single pointer to struct with flexible array member. We do not + // promote unsafe_indexable here but if its flexible array member is + // annotated with counted_by, the checks will still be performed based on the + // count value when the member is dereferenced. + // `DisableFlexibleArrayPromotion` allows us to skip the promotion for a base + // of member expression as it can create a problem of incorrect bounds while + // the member is being updated and it is redundant. When the base is a + // single pointer to struct with flexible array member, other members are safe + // to access without extra checks just like any other single pointers. + // Directly accessing the flexible array member will still be promoted to + // bidi_indexable and will be safely handled. + if (getLangOpts().BoundsSafety && !DisableFlexibleArrayPromotion) { + if (auto *RD = getImmediateDeclForFlexibleArrayPromotion(T)) { + Res = PromoteBoundsSafetyPointerToFlexibleArrayMember(*this, RD, Res.get()); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Res; } -ExprResult Sema::DefaultFunctionArrayLvalueConversion(Expr *E, bool Diagnose) { - ExprResult Res = DefaultFunctionArrayConversion(E, Diagnose); +ExprResult Sema::DefaultFunctionArrayLvalueConversion( + Expr *E, bool Diagnose, bool DiagnoseBoundsSafetyIncompleteArrayPromotion, + bool DisableFlexibleArrayPromotion) { + ExprResult Res = DefaultFunctionArrayConversion( + E, Diagnose, DiagnoseBoundsSafetyIncompleteArrayPromotion, + DisableFlexibleArrayPromotion); if (Res.isInvalid()) return ExprError(); - Res = DefaultLvalueConversion(Res.get()); + Res = DefaultLvalueConversion(Res.get(), DisableFlexibleArrayPromotion); if (Res.isInvalid()) return ExprError(); return Res; @@ -868,6 +1299,22 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { return ExprError(); E = Res.get(); + // The argument we are processing is for a function without a prototype + // or a varargs argument. Passing a pointer with bounds will very likely lead + // to an ABI mismatch because there's a good chance the implementing function + // isn't using -fbounds-safety. Avoid this potential ABI mismatch by not allowing a + // pointer with bounds to passed by casting to an unsafeIndexable pointer + // which is ABI compatible with normal pointers. + if (E->getType()->isPointerTypeWithBounds()) { + QualType UnsafePointerTy = Context.getBoundsSafetyPointerType( + E->getType(), BoundsSafetyPointerAttributes::unsafeIndexable()); + ExprResult ExprRes = + ImpCastExprToType(E, UnsafePointerTy, CK_BoundsSafetyPointerCast); + if (ExprRes.isInvalid()) + return ExprError(); + E = ExprRes.get(); + } + // If this is a 'float' or '__fp16' (CVR qualified or typedef) // promote to double. // Note that default argument promotion applies only to float (and @@ -1556,12 +2003,31 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, QualType LHSType = LHS.get()->getType().getUnqualifiedType(); QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + // BoundsSafety: "pointer" op "pointer" -> cast it to raw pointer. + auto *LPTy = LHSType->getAs(); + auto *RPTy = RHSType->getAs(); + if (ACK != ACK_Conditional && LPTy && RPTy && + (!LPTy->hasRawPointerLayout() || !RPTy->hasRawPointerLayout())) { + if (!LPTy->hasRawPointerLayout()) + LHS = ImpCastExprToType(LHS.get(), + Context.getPointerType(LPTy->getPointeeType()), CK_BoundsSafetyPointerCast); + if (!RPTy->hasRawPointerLayout()) + RHS = ImpCastExprToType(RHS.get(), + Context.getPointerType(RPTy->getPointeeType()), CK_BoundsSafetyPointerCast); + } + // For conversion purposes, we ignore any atomic qualifier on the LHS. if (const AtomicType *AtomicLHS = LHSType->getAs()) LHSType = AtomicLHS->getValueType(); // If both types are identical, no conversion is needed. - if (Context.hasSameType(LHSType, RHSType)) + if (Context.hasSameType(LHSType, RHSType) + /* TO_UPSTREAM(BoundsSafety) ON*/ + && (!Context.getLangOpts().BoundsSafety || + Context.canMergeTypeBounds(LHSType, RHSType) == + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) return Context.getCommonSugaredType(LHSType, RHSType); // If either side is a non-arithmetic type (e.g. a pointer), we are done. @@ -1580,7 +2046,13 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, LHS = ImpCastExprToType(LHS.get(), LHSType, CK_IntegralCast); // If both types are identical, no conversion is needed. - if (Context.hasSameType(LHSType, RHSType)) + if (Context.hasSameType(LHSType, RHSType) + /* TO_UPSTREAM(BoundsSafety) ON*/ + && (!Context.getLangOpts().BoundsSafety || + Context.canMergeTypeBounds(LHSType, RHSType) == + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) return Context.getCommonSugaredType(LHSType, RHSType); // At this point, we have two different arithmetic types. @@ -4458,6 +4930,10 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T, case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ // Keep walking after single level desugaring. T = T.getSingleStepDesugaredType(Context); break; @@ -5124,6 +5600,45 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, // and index from the expression types. Expr *BaseExpr, *IndexExpr; QualType ResultType; + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto diagnoseBoundsSafetyPointerSubscript = [&](QualType Ty) -> bool { + const PointerType *PTy = Ty->getAs(); + // If the base of the array subscript expression is 'counted_by', it should + // have been promoted to __bidi_indexable. + assert(!Ty->isBoundsAttributedType()); + if (PTy->isSingle()) { + Expr::EvalResult RInt; + if (!RHSExp->EvaluateAsInt(RInt, Context) || RInt.Val.getInt() != 0) { + Diag(LLoc, diag::err_bounds_safety_pointer_subscript) + << Ty->isValueTerminatedType() << BaseExpr + << SourceRange(LHSExp->getBeginLoc(), RHSExp->getEndLoc()); + return false; + } + } else if (PTy->isIndexable() && + !IndexExpr->getType()->isUnsignedIntegerType()) { + Expr::EvalResult RInt; + // Runtime check will be inserted for the indices whose negativity can't + // be known statically. + if (RHSExp->EvaluateAsInt(RInt, Context) && RInt.Val.getInt() < 0) { + Diag(LLoc, diag::err_bounds_safety_indexable_pointer_subscript) + << BaseExpr + << SourceRange(LHSExp->getBeginLoc(), RHSExp->getEndLoc()); + return false; + } + } + return true; + }; + + auto castIndexableToBidiIndexable = [&](Expr *BaseExpr) -> Expr * { + if (!BaseExpr->getType()->isIndexablePointerType()) + return BaseExpr; + QualType Ty = Context.getBoundsSafetyPointerType( + BaseExpr->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + return ImpCastExprToType(BaseExpr, Ty, CK_BoundsSafetyPointerCast).get(); + }; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LHSTy->isDependentType() || RHSTy->isDependentType()) { BaseExpr = LHSExp; IndexExpr = RHSExp; @@ -5133,6 +5648,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !diagnoseBoundsSafetyPointerSubscript(LHSTy)) + return ExprError(); + LHSExp = castIndexableToBidiIndexable(BaseExpr); + LHSTy = LHSExp->getType(); + BaseExpr = LHSExp; + /*TO_UPSTREAM(BoundsSafety) OFF*/ } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; @@ -5150,6 +5672,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = RHSExp; IndexExpr = LHSExp; ResultType = PTy->getPointeeType(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !diagnoseBoundsSafetyPointerSubscript(RHSTy)) + return ExprError(); + RHSExp = castIndexableToBidiIndexable(BaseExpr); + RHSTy = RHSExp->getType(); + BaseExpr = RHSExp; + /*TO_UPSTREAM(BoundsSafety) OFF*/ } else if (const ObjCObjectPointerType *PTy = RHSTy->getAs()) { // Handle the uncommon case of "123[Ptr]". @@ -5691,6 +6220,50 @@ class FunctionCallCCC final : public FunctionCallFilterCCC { }; } +namespace { +class ContainsBoundsAttributedType final + : public TypeVisitor { + +public: + static bool check(QualType QT) { + return ContainsBoundsAttributedType().Visit(QT.getTypePtr()); + } + + bool VisitType(const Type *T) { + QualType Desugared = T->getLocallyUnqualifiedSingleStepDesugaredType(); + return Desugared.getTypePtr() == T ? false : Visit(Desugared.getTypePtr()); + } + + bool VisitPointerType(const PointerType *T) { + if (getImmediateDeclForFlexibleArrayPromotion(QualType(T,0))) + return true; + auto Pointee = T->getPointeeType(); + if (!Pointee->isIncompleteOrObjectType()) + return false; + return Visit(Pointee.getTypePtr()); + } + + bool VisitFunctionType(const FunctionType *T) { + return Visit(T->getReturnType().getTypePtr()); + } + + bool VisitFunctionProtoType(const FunctionProtoType *T) { + if (VisitFunctionType(T)) + return true; + for (QualType PT : T->getParamTypes()) + if (Visit(PT.getTypePtr())) + return true; + return false; + } + + bool VisitBoundsAttributedType(const BoundsAttributedType *T) { + return true; + } +}; + + +} + static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn, FunctionDecl *FDecl, ArrayRef Args) { @@ -5908,6 +6481,17 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, if (ArgIx < Args.size()) { Arg = Args[ArgIx++]; + /*TO_UPSTREAM(BoundsSafety) ON*/ + // It's not useful to throw type errors based on the type of most + // expressions with errors. Make an exception for DeclRefExprs since the + // type of the Decl is explicit. The type of casts would be explicit also, + // but the bounds type can be implicit in -fbounds-safety, which is the + // target of this (normal C is way more lax with type errors) + if (getLangOpts().BoundsSafety && Arg->containsErrors() && + !isa(Arg->IgnoreParenCasts())) + continue; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (RequireCompleteType(Arg->getBeginLoc(), ProtoArgType, diag::err_call_incomplete_argument, Arg)) return true; @@ -6170,8 +6754,10 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, for (QualType ParamType : FT->param_types()) { // Convert array arguments to pointer to simplify type lookup. - ExprResult ArgRes = - Sema->DefaultFunctionArrayLvalueConversion(ArgExprs[i++]); + ExprResult ArgRes = Sema->DefaultFunctionArrayLvalueConversion( + ArgExprs[i++], /*Diagnose=*/true, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion=*/true, + /*DisableFlexibleArrayPromotion=*/true); if (ArgRes.isInvalid()) return nullptr; Expr *Arg = ArgRes.get(); @@ -6388,6 +6974,12 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, if (LangOpts.OpenMP) Call = OpenMP().ActOnOpenMPCall(Call, Scope, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + Call = ActOnBoundsSafetyCall(Call); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LangOpts.CPlusPlus) { if (const auto *CE = dyn_cast(Call.get())) DiagnosedUnqualifiedCallsToStdFunctions(*this, CE); @@ -6654,14286 +7246,18774 @@ ExprResult Sema::ActOnConvertVectorExpr(Expr *E, ParsedType ParsedDestTy, return ConvertVectorExpr(E, TInfo, BuiltinLoc, RParenLoc); } -ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, - SourceLocation LParenLoc, - ArrayRef Args, - SourceLocation RParenLoc, Expr *Config, - bool IsExecConfig, ADLCallKind UsesADL) { - FunctionDecl *FDecl = dyn_cast_or_null(NDecl); - unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); - - // Functions with 'interrupt' attribute cannot be called directly. - if (FDecl) { - if (FDecl->hasAttr()) { - Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); - return ExprError(); - } - if (FDecl->hasAttr()) { - Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called); - return ExprError(); +/* TO_UPSTREAM(BoundsSafety) ON*/ +static const ValueDecl *findValueDecl(const Expr *E, int *AddrOfDerefDiff) { + if (AddrOfDerefDiff) + *AddrOfDerefDiff = 0; + E = E->IgnoreParenCasts(); + int AddrOfDiff = 0; + while (auto UO = dyn_cast(E)) { + switch (UO->getOpcode()) { + case UO_AddrOf: + AddrOfDiff++; + break; + case UO_Deref: + AddrOfDiff--; + break; + default: + return nullptr; } + E = UO->getSubExpr()->IgnoreParenCasts(); } + /// Not supported as dynamic count or pointer arguments. + if (AddrOfDiff > 1 || AddrOfDiff < -1) + return nullptr; - // X86 interrupt handlers may only call routines with attribute - // no_caller_saved_registers since there is no efficient way to - // save and restore the non-GPR state. - if (auto *Caller = getCurFunctionDecl()) { - if (Caller->hasAttr() || - Caller->hasAttr()) { - const TargetInfo &TI = Context.getTargetInfo(); - bool HasNonGPRRegisters = - TI.hasFeature("sse") || TI.hasFeature("x87") || TI.hasFeature("mmx"); - if (HasNonGPRRegisters && - (!FDecl || !FDecl->hasAttr())) { - Diag(Fn->getExprLoc(), diag::warn_anyx86_excessive_regsave) - << (Caller->hasAttr() ? 0 : 1); - if (FDecl) - Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl; - } + if (AddrOfDerefDiff) + *AddrOfDerefDiff = AddrOfDiff; + + if (auto ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (auto DRE = dyn_cast(E)) + return DRE->getDecl(); + return nullptr; +} + +static const RecordDecl *findBaseRecordDecl(const Expr *E) { + E = E->IgnoreParenCasts(); + while (auto UO = dyn_cast(E)) { + switch (UO->getOpcode()) { + case UO_AddrOf: + case UO_Deref: + break; + default: + return nullptr; } + E = UO->getSubExpr()->IgnoreParenCasts(); } - // Promote the function operand. - // We special-case function promotion here because we only allow promoting - // builtin functions to function pointers in the callee of a call. - ExprResult Result; - QualType ResultTy; - if (BuiltinID && - Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) { - // Extract the return type from the (builtin) function pointer type. - // FIXME Several builtins still have setType in - // Sema::CheckBuiltinFunctionCall. One should review their definitions in - // Builtins.td to ensure they are correct before removing setType calls. - QualType FnPtrTy = Context.getPointerType(FDecl->getType()); - Result = ImpCastExprToType(Fn, FnPtrTy, CK_BuiltinFnToFnPtr).get(); - ResultTy = FDecl->getCallResultType(); - } else { - Result = CallExprUnaryConversions(Fn); - ResultTy = Context.BoolTy; + RecordDecl *BaseRecord = nullptr; + while (auto ME = dyn_cast(E)) { + E = ME->getBase()->IgnoreParenCasts(); + const Type *BaseTy = E->getType().getTypePtr(); + if (ME->isArrow()) + BaseTy = BaseTy->getPointeeOrArrayElementType(); + BaseRecord = BaseTy->getAsRecordDecl(); } - if (Result.isInvalid()) - return ExprError(); - Fn = Result.get(); + return BaseRecord; +} - // Check for a valid function type, but only if it is not a builtin which - // requires custom type checking. These will be handled by - // CheckBuiltinFunctionCall below just after creation of the call expression. - const FunctionType *FuncT = nullptr; - if (!BuiltinID || !Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { - retry: - if (const PointerType *PT = Fn->getType()->getAs()) { - // C99 6.5.2.2p1 - "The expression that denotes the called function shall - // have type pointer to function". - FuncT = PT->getPointeeType()->getAs(); - if (!FuncT) - return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) - << Fn->getType() << Fn->getSourceRange()); - } else if (const BlockPointerType *BPT = - Fn->getType()->getAs()) { - FuncT = BPT->getPointeeType()->castAs(); - } else { - // Handle calls to expressions of unknown-any type. - if (Fn->getType() == Context.UnknownAnyTy) { - ExprResult rewrite = rebuildUnknownAnyFunction(*this, Fn); - if (rewrite.isInvalid()) - return ExprError(); - Fn = rewrite.get(); - goto retry; - } +static const ValueDecl *findValueDecl(const Expr *E, bool *AsAddrOf = nullptr, + bool *AsDeref = nullptr) { + int AddrOfDerefDiff = 0; + const auto *VD = findValueDecl(E, &AddrOfDerefDiff); + if (AsAddrOf) + *AsAddrOf = AddrOfDerefDiff == 1; + if (AsDeref) + *AsDeref = AddrOfDerefDiff == -1; + return VD; +} - return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) - << Fn->getType() << Fn->getSourceRange()); - } +namespace { +class DynamicCountExprProfiler : public ConstStmtVisitor { + llvm::FoldingSetNodeID &ID; + const ASTContext &Context; +public: + DynamicCountExprProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context) + : ID(ID), Context(Context) {} + + void VisitCastExpr(const CastExpr *E) { + Visit(E->getSubExpr()); } - // Get the number of parameters in the function prototype, if any. - // We will allocate space for max(Args.size(), NumParams) arguments - // in the call expression. - const auto *Proto = dyn_cast_or_null(FuncT); - unsigned NumParams = Proto ? Proto->getNumParams() : 0; + void VisitStmt(const Stmt *S) { + assert(S && "Requires non-null Stmt pointer"); - CallExpr *TheCall; - if (Config) { - assert(UsesADL == ADLCallKind::NotADL && - "CUDAKernelCallExpr should not use ADL"); - TheCall = CUDAKernelCallExpr::Create(Context, Fn, cast(Config), - Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams); - } else { - TheCall = - CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams, UsesADL); + HandleStmtClass(S->getStmtClass()); + + for (const Stmt *SubStmt : S->children()) { + if (SubStmt) + Visit(SubStmt); + else + ID.AddInteger(0); + } } - if (!Context.isDependenceAllowed()) { - // Forget about the nulled arguments since typo correction - // do not handle them well. - TheCall->shrinkNumArgs(Args.size()); - // C cannot always handle TypoExpr nodes in builtin calls and direct - // function calls as their argument checking don't necessarily handle - // dependent types properly, so make sure any TypoExprs have been - // dealt with. - ExprResult Result = CorrectDelayedTyposInExpr(TheCall); - if (!Result.isUsable()) return ExprError(); - CallExpr *TheOldCall = TheCall; - TheCall = dyn_cast(Result.get()); - bool CorrectedTypos = TheCall != TheOldCall; - if (!TheCall) return Result; - Args = llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()); + void HandleStmtClass(Stmt::StmtClass SC) { + ID.AddInteger(SC); + } - // A new call expression node was created if some typos were corrected. - // However it may not have been constructed with enough storage. In this - // case, rebuild the node with enough storage. The waste of space is - // immaterial since this only happens when some typos were corrected. - if (CorrectedTypos && Args.size() < NumParams) { - if (Config) - TheCall = CUDAKernelCallExpr::Create( - Context, Fn, cast(Config), Args, ResultTy, VK_PRValue, - RParenLoc, CurFPFeatureOverrides(), NumParams); - else - TheCall = - CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams, UsesADL); + void HandleDeclReference(const Expr *E) { + ID.AddInteger(Stmt::DeclRefExprClass); + VisitType(E->getType()); + } + + void VisitType(QualType T) { + if (!T.isNull()) + T = Context.getCanonicalType(T); + + ID.AddPointer(T.getAsOpaquePtr()); + } + + void VisitIdentifierInfo(IdentifierInfo *II) { + ID.AddPointer(II); + } + void VisitDeclRefExpr(const DeclRefExpr *E) { + HandleDeclReference(E); + } + void VisitMemberExpr(const MemberExpr *E) { + HandleDeclReference(E); + } + void VisitUnaryOperator(const UnaryOperator *E) { + if (E->getOpcode() == UO_Deref) { + HandleDeclReference(E); + return; } - // We can now handle the nulled arguments for the default arguments. - TheCall->setNumArgsUnsafe(std::max(Args.size(), NumParams)); + VisitStmt(E); } +}; +} - // Bail out early if calling a builtin with custom type checking. - if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { - ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); - if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID)) - E = CheckForImmediateInvocation(E, FDecl); - return E; +static bool compatibleDynamicCountExprs(const ASTContext &Context, Expr *LHS, Expr *RHS) { + llvm::FoldingSetNodeID LID; + llvm::FoldingSetNodeID RID; + DynamicCountExprProfiler(LID, Context).Visit(LHS); + DynamicCountExprProfiler(RID, Context).Visit(RHS); + return LID == RID; +} + +namespace { + +struct CheckSubstitutedCountExpr + : public ConstStmtVisitor { + using BaseVisitor = ConstStmtVisitor; + const ParmVarDecl *ArgPtrParmDecl; + const MemberExpr *ArgPtrMemberExpr; + + explicit CheckSubstitutedCountExpr(const ParmVarDecl *PtrParmDecl, + const MemberExpr *PtrMemberExpr) + : ArgPtrParmDecl(PtrParmDecl), ArgPtrMemberExpr(PtrMemberExpr){}; + + bool VisitStmt(const Stmt *S) { return false; } + + bool VisitImplicitCastExpr(const ImplicitCastExpr *E) { + return Visit(E->getSubExpr()); } - if (getLangOpts().CUDA) { - if (Config) { - // CUDA: Kernel calls must be to global functions - if (FDecl && !FDecl->hasAttr()) - return ExprError(Diag(LParenLoc,diag::err_kern_call_not_global_function) - << FDecl << Fn->getSourceRange()); + bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } - // CUDA: Kernel function must have 'void' return type - if (!FuncT->getReturnType()->isVoidType() && - !FuncT->getReturnType()->getAs() && - !FuncT->getReturnType()->isInstantiationDependentType()) - return ExprError(Diag(LParenLoc, diag::err_kern_type_not_void_return) - << Fn->getType() << Fn->getSourceRange()); - } else { - // CUDA: Calls to global functions must be configured - if (FDecl && FDecl->hasAttr()) - return ExprError(Diag(LParenLoc, diag::err_global_call_not_config) - << FDecl << Fn->getSourceRange()); + bool VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + return Visit(E->getSourceExpr()); + } + + bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } + + bool VisitUnaryOperator(const UnaryOperator *E) { + switch (E->getOpcode()) { + case UO_AddrOf: + case UO_Deref: + return Visit(E->getSubExpr()); + default: + // An invalid opcode. + return false; } } - // Check for a valid return type - if (CheckCallReturnType(FuncT->getReturnType(), Fn->getBeginLoc(), TheCall, - FDecl)) - return ExprError(); + bool VisitBinaryOperator(const BinaryOperator *E) { + // TODO: This blocks any binary operator. This is done on purpose, since we + // don't have CodeGen tests with complex count expressions. Once we have + // them, we can use the commented out code below. + // rdar://119737451 + return false; - // We know the result type of the call, set it. - TheCall->setType(FuncT->getCallResultType(Context)); - TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); + /* + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + if (!LHS->getType()->isIntegerType() || !RHS->getType()->isIntegerType()) + return false; + if (!Visit(LHS)) + return false; + return Visit(RHS); + */ + } - // WebAssembly tables can't be used as arguments. - if (Context.getTargetInfo().getTriple().isWasm()) { - for (const Expr *Arg : Args) { - if (Arg && Arg->getType()->isWebAssemblyTableType()) { - return ExprError(Diag(Arg->getExprLoc(), - diag::err_wasm_table_as_function_parameter)); + // Check if the decls in the count argument are parameters of the same + // function as the pointer argument. + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (!ArgPtrParmDecl) + return false; + const auto *PVD = dyn_cast(E->getDecl()); + if (!PVD || PVD->getDeclContext() != ArgPtrParmDecl->getDeclContext()) + return false; + return true; + } + + // Check if the member expressions in the count argument have the same base as + // the pointer argument. + bool VisitMemberExpr(const MemberExpr *E) { + if (!ArgPtrMemberExpr) + return false; + + const auto *Ptr = ArgPtrMemberExpr->getBase(); + const auto *Count = E->getBase(); + for (;;) { + if (Ptr == Count) + return true; + const auto *PtrICE = dyn_cast(Ptr); + const auto *CountICE = dyn_cast(Count); + if (PtrICE && CountICE && PtrICE->getCastKind() == CK_LValueToRValue && + CountICE->getCastKind() == CK_LValueToRValue) { + Ptr = PtrICE->getSubExpr(); + Count = CountICE->getSubExpr(); } + const auto *PtrDRE = dyn_cast(Ptr); + const auto *CountDRE = dyn_cast(Count); + if (PtrDRE && CountDRE) + return PtrDRE->getDecl() == CountDRE->getDecl(); + const auto *PtrME = dyn_cast(Ptr); + const auto *CountME = dyn_cast(Count); + if (!PtrME || !CountME) + return false; + if (PtrME->getMemberDecl() != CountME->getMemberDecl()) + return false; + Ptr = PtrME->getBase(); + Count = CountME->getBase(); } } +}; - if (Proto) { - if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, - IsExecConfig)) - return ExprError(); - } else { - assert(isa(FuncT) && "Unknown FunctionType!"); +struct SubstitutedCountExprPrinterHelper : public PrinterHelper { + const PrintingPolicy &Policy; + const llvm::SmallPtrSetImpl *ExprsNeedingParens; - if (FDecl) { - // Check if we have too few/too many template arguments, based - // on our knowledge of the function definition. - const FunctionDecl *Def = nullptr; - if (FDecl->hasBody(Def) && Args.size() != Def->param_size()) { - Proto = Def->getType()->getAs(); - if (!Proto || !(Proto->isVariadic() && Args.size() >= Def->param_size())) - Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) - << (Args.size() > Def->param_size()) << FDecl << Fn->getSourceRange(); + explicit SubstitutedCountExprPrinterHelper( + const PrintingPolicy &Policy, + const llvm::SmallPtrSetImpl *ExprsNeedingParens) + : Policy(Policy), ExprsNeedingParens(ExprsNeedingParens) {} + + bool handledStmt(Stmt *S, raw_ostream &OS) override { + // Print the field decl. + if (const auto *ME = dyn_cast(S)) { + ME->getMemberDecl()->printName(OS); + return true; + } + + // Simplify *&decl. + if (const auto *Outer = dyn_cast(S); + Outer && Outer->getOpcode() == UO_Deref) { + if (const auto *Inner = dyn_cast( + Outer->getSubExpr()->IgnoreParenImpCasts()); + Inner && Inner->getOpcode() == UO_AddrOf) { + if (const auto *DRE = dyn_cast( + Inner->getSubExpr()->IgnoreParenImpCasts())) { + DRE->getDecl()->printName(OS); + return true; + } } + } - // If the function we're calling isn't a function prototype, but we have - // a function prototype from a prior declaratiom, use that prototype. - if (!FDecl->hasPrototype()) - Proto = FDecl->getType()->getAs(); + // Wrap in parentheses if needed. + if (ExprsNeedingParens) { + const auto *E = dyn_cast(S); + if (ExprsNeedingParens->contains(E)) { + OS << '('; + SubstitutedCountExprPrinterHelper Helper(Policy, nullptr); + E->printPretty(OS, &Helper, Policy); + OS << ')'; + return true; + } } - // If we still haven't found a prototype to use but there are arguments to - // the call, diagnose this as calling a function without a prototype. - // However, if we found a function declaration, check to see if - // -Wdeprecated-non-prototype was disabled where the function was declared. - // If so, we will silence the diagnostic here on the assumption that this - // interface is intentional and the user knows what they're doing. We will - // also silence the diagnostic if there is a function declaration but it - // was implicitly defined (the user already gets diagnostics about the - // creation of the implicit function declaration, so the additional warning - // is not helpful). - if (!Proto && !Args.empty() && - (!FDecl || (!FDecl->isImplicit() && - !Diags.isIgnored(diag::warn_strict_uses_without_prototype, - FDecl->getLocation())))) - Diag(LParenLoc, diag::warn_strict_uses_without_prototype) - << (FDecl != nullptr) << FDecl; + // Print normally. + return false; + } +}; - // Promote the arguments (C99 6.5.2.2p6). - for (unsigned i = 0, e = Args.size(); i != e; i++) { - Expr *Arg = Args[i]; +// Try to emit a fixit when an implicit __single pointer is assigned to a +// dynamic count pointer. The fixit should contain an appropriate dynamic bound +// annotation that can be attached to the implicit __single pointer. +void TryFixSingleToDynamicCount( + Sema &S, const CountAttributedType *ParmPtrTy, const Expr *ArgPtrExpr, + const Expr *CountExpr, + const llvm::SmallPtrSetImpl &ReplacingValues) { + // Extract the decl from the pointer argument passed to dynamic count pointer + // param. + const ParmVarDecl *ArgPtrParmDecl = nullptr; + const MemberExpr *ArgPtrMemberExpr = nullptr; + const DeclaratorDecl *ArgPtrDecl = nullptr; + const Expr *P = ArgPtrExpr->IgnoreParenImpCasts(); + if (const auto *ME = dyn_cast(P)) { + ArgPtrMemberExpr = ME; + ArgPtrDecl = dyn_cast(ME->getMemberDecl()); + } else if (const auto *DRE = dyn_cast(P)) { + ArgPtrDecl = ArgPtrParmDecl = dyn_cast(DRE->getDecl()); + } + if (!ArgPtrDecl) + return; - if (Proto && i < Proto->getNumParams()) { - InitializedEntity Entity = InitializedEntity::InitializeParameter( - Context, Proto->getParamType(i), Proto->isParamConsumed(i)); - ExprResult ArgE = - PerformCopyInitialization(Entity, SourceLocation(), Arg); - if (ArgE.isInvalid()) - return true; + QualType ArgPtrTy = ArgPtrDecl->getType(); + // This should be called only if the pointer argument is 'unbounded'. + assert(ArgPtrTy->isSinglePointerType() && + !ArgPtrTy->isBoundsAttributedType() && + !ArgPtrTy->isValueTerminatedType()); - Arg = ArgE.getAs(); + // Don't emit a fixit if the decl has an explicit attr. + if (!ArgPtrTy->hasAttr(attr::PtrAutoAttr)) + return; - } else { - ExprResult ArgE = DefaultArgumentPromotion(Arg); + // Don't emit a fixit for __counted_by() when the argument pointer has a + // different pointee. + if (!ParmPtrTy->isCountInBytes() && + !S.Context.hasSameUnqualifiedType(ArgPtrTy->getPointeeType(), + ParmPtrTy->getPointeeType())) + return; - if (ArgE.isInvalid()) - return true; + // Check if the count argument is valid. + CheckSubstitutedCountExpr Check(ArgPtrParmDecl, ArgPtrMemberExpr); + bool Valid = Check.Visit(CountExpr); + if (!Valid) + return; - Arg = ArgE.getAs(); + // Wrap each count argument in parentheses if the callee's __counted_by() + // expression and the passed argument are not simple enough. + llvm::SmallPtrSet ExprsNeedingParens; + if (!isa(ParmPtrTy->getCountExpr()->IgnoreImpCasts())) { + for (const auto *Expr : ReplacingValues) { + const auto *E = Expr->IgnoreParenImpCasts(); + bool NeedsParens = true; + if (isa(E) || isa(E)) { + NeedsParens = false; + } else if (const auto *UO = dyn_cast(E)) { + NeedsParens = !(UO->getOpcode() == UO_AddrOf && + isa(UO->getSubExpr())); } + if (NeedsParens) + ExprsNeedingParens.insert(Expr); + } + } + + PrintingPolicy Policy(S.getLangOpts()); + SubstitutedCountExprPrinterHelper Helper(Policy, &ExprsNeedingParens); + + llvm::SmallString<32> Code; + llvm::raw_svector_ostream OS(Code); + OS << "__" + << (ParmPtrTy->isCountInBytes() + ? (ParmPtrTy->isOrNull() ? "sized_by_or_null" : "sized_by") + : (ParmPtrTy->isOrNull() ? "counted_by_or_null" : "counted_by")) + << '('; + CountExpr->printPretty(OS, &Helper, Policy); + OS << ") "; + StringRef FixIt = OS.str(); + StringRef Attr = FixIt.drop_back(); + + auto [FixItLoc, NeedsSpaceAfterAttr] = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint( + ArgPtrDecl->getTypeSourceInfo()->getTypeLoc(), S); + if (FixItLoc.isInvalid()) + return; + if (!NeedsSpaceAfterAttr) + FixIt = FixIt.drop_back(); - if (RequireCompleteType(Arg->getBeginLoc(), Arg->getType(), - diag::err_call_incomplete_argument, Arg)) - return ExprError(); + S.Diag(ArgPtrDecl->getLocation(), diag::note_bounds_safety_consider_adding_to) + << ArgPtrDecl->getName() << Attr + << FixItHint::CreateInsertion(FixItLoc, FixIt); +} - TheCall->setArg(i, Arg); - } - TheCall->computeDependence(); +bool checkDynamicCountSizeForAssignmentWithUnknownCount( + Sema &S, QualType LHSTy, const Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, bool UnboundedRHS, + const Expr *CountExpr, + const llvm::SmallPtrSetImpl &ReplacingValues) { + if (!UnboundedRHS) + return true; + + const auto *LDCPTy = LHSTy->getAs(); + QualType RHSTy = RHSExpr->getType(); + + QualType RHSElemTy = RHSTy->getPointeeType(); + int64_t ElementByteSize = S.Context.getTypeSizeInCharsIfKnown(RHSElemTy) + .value_or(CharUnits::Zero()) + .getQuantity(); + assert(ElementByteSize >= 0); + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::warn_bounds_safety_dynamic_count_from_unbounded) + << Action << LHSTy << LDCPTy->isCountInBytes() << LDCPTy->isOrNull() + << RHSTy << !DesignatorStr.empty() << DesignatorStr << ElementByteSize + << CountExpr->getSourceRange(); + + // The count is not returned, don't say 'count returned here'. + if (Action != Sema::AA_Returning) { + S.Diag(CountExpr->getBeginLoc(), + diag::note_bounds_safety_dynamic_count_from_unbounded_count_location) + << Action << LDCPTy->isCountInBytes(); } - if (CXXMethodDecl *Method = dyn_cast_or_null(FDecl)) - if (Method->isImplicitObjectMemberFunction()) - return ExprError(Diag(LParenLoc, diag::err_member_call_without_object) - << Fn->getSourceRange() << 0); + TryFixSingleToDynamicCount(S, LDCPTy, RHSExpr, CountExpr, ReplacingValues); + return true; +} - // Check for sentinels - if (NDecl) - DiagnoseSentinelCalls(NDecl, LParenLoc, Args); +bool checkDynamicCountSizeForAssignmentWithKnownCount( + Sema &S, QualType LHSTy, const Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, + const Sema::DependentValuesMap &DependentValues, bool UnboundedRHS, + const Expr *CountExpr, const llvm::APSInt &CountVal, + const llvm::SmallPtrSetImpl &ReplacingValues) { + QualType RHSTy = RHSExpr->getType(); + const auto *LDCPTy = LHSTy->getAs(); + const bool IsOrNull = LDCPTy->isOrNull(); + + const auto *DRE = dyn_cast(RHSExpr->IgnoreParenCasts()); + const ConstantArrayType *CAT = nullptr; + if (DRE) + CAT = S.Context.getAsConstantArrayType(DRE->getDecl()->getType()); + + const bool IsImplicitInitExpr = isa(RHSExpr); + const bool IsNull = + IsImplicitInitExpr || RHSExpr->isNullPointerConstant( + S.Context, Expr::NPC_ValueDependentIsNotNull); + const bool IsNonnull = CAT; // Array decays to nonnull pointer. + + // If the count is negative, always emit an error for __counted_by(). In + // addition, emit this error for __counted_by_or_null() if the RHS is known to + // be nonnull pointer. + if (CountVal < 0 && (!IsOrNull || IsNonnull)) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_negative) + << LHSTy << LDCPTy->isCountInBytes() << CountVal.getSExtValue() + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + return false; + } - // Warn for unions passing across security boundary (CMSE). - if (FuncT != nullptr && FuncT->getCmseNSCallAttr()) { - for (unsigned i = 0, e = Args.size(); i != e; i++) { - if (const auto *RT = - dyn_cast(Args[i]->getType().getCanonicalType())) { - if (RT->getDecl()->isOrContainsUnion()) - Diag(Args[i]->getBeginLoc(), diag::warn_cmse_nonsecure_union) - << 0 << i; + if (CountVal > 0) { + const uint64_t LHSCount = CountVal.getZExtValue(); + uint64_t LHSSize = LHSCount; + if (!LDCPTy->isCountInBytes()) { + LHSSize *= S.Context.getTypeSizeInCharsIfKnown(LDCPTy->getPointeeType()) + .value_or(CharUnits::Zero()) + .getQuantity(); + } + + // The RHS is a constant array, check the count. + if (CAT) { + bool InBytes = LDCPTy->isCountInBytes() || + !S.Context.hasSameUnqualifiedType(LDCPTy->getPointeeType(), + CAT->getElementType()); + uint64_t NumLHS = LHSCount; + uint64_t NumRHS = CAT->getSize().getZExtValue(); + if (InBytes) { + NumLHS = LHSSize; + NumRHS *= + S.Context.getTypeSizeInChars(CAT->getElementType()).getQuantity(); + } + if (NumRHS < NumLHS) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_array_bad_size) + << Action << LHSTy << InBytes << DRE->getDecl() << NumLHS << NumRHS + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + S.Diag(DRE->getDecl()->getLocation(), diag::note_entity_declared_at) + << DRE->getDecl(); + return false; } } - } - - // Do special checking on direct calls to functions. - if (FDecl) { - if (CheckFunctionCall(FDecl, TheCall, Proto)) - return ExprError(); - checkFortifiedBuiltinMemoryFunction(FDecl, TheCall); + // __single to __counted_by()/__sized_by(), check the pointee size. Don't + // emit an error for *_or_null(), since the RHS might be null. + if (UnboundedRHS && !IsOrNull) { + QualType RHSElemTy = RHSTy->getPointeeType(); + bool InBytes = LDCPTy->isCountInBytes() || + !S.Context.hasSameUnqualifiedType(LDCPTy->getPointeeType(), + RHSElemTy); + uint64_t NumLHS = LHSCount; + uint64_t NumRHS = 1; + if (InBytes) { + NumLHS = LHSSize; + // For types with unknown size (e.g., opaque types) we assume their size + // is 0 and emit an error right now rather than trapping at runtime + // (CodeGen would use 0 as their size anyway and this assignment would + // fail for any __sized_by() type with positive count). + CharUnits RHSElemSize = + S.Context.getTypeSizeInCharsIfKnown(RHSElemTy).value_or( + CharUnits::Zero()); + NumRHS *= RHSElemSize.getQuantity(); + } + if (NumRHS < NumLHS) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_unbounded_bad_size) + << Action << LHSTy << InBytes << RHSTy << NumLHS << NumRHS + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + TryFixSingleToDynamicCount(S, LDCPTy, RHSExpr, CountExpr, + ReplacingValues); + return false; + } + } - if (BuiltinID) - return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); - } else if (NDecl) { - if (CheckPointerCall(NDecl, TheCall, Proto)) - return ExprError(); - } else { - if (CheckOtherCall(TheCall, Proto)) - return ExprError(); + // NULL to __counted_by()/__sized_by() with a positive count. + if (IsNull && !IsOrNull) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_null_nonzero_count) + << Action << LHSTy << LDCPTy->isCountInBytes() + << CountVal.getZExtValue() << !DesignatorStr.empty() << DesignatorStr + << IsImplicitInitExpr << CountExpr->getSourceRange(); + return false; + } } - return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); -} - -ExprResult -Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, - SourceLocation RParenLoc, Expr *InitExpr) { - assert(Ty && "ActOnCompoundLiteral(): missing type"); - assert(InitExpr && "ActOnCompoundLiteral(): missing expression"); + // The pointer is set to some value, but the count is implicitly initialized + // to 0 (e.g., in struct initializer). This is likely to be a user's mistake, + // thus we emit a warning. + bool ImplicitCount = std::any_of(DependentValues.begin(), + DependentValues.end(), [](const auto &Item) { + const Expr *E = Item.second.first; + return isa(E); + }); + if (CountVal == 0 && !IsNull && ImplicitCount) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, + diag::warn_bounds_safety_dynamic_count_from_nonnull_implicit_zero_count) + << Action << LHSTy << LDCPTy->isCountInBytes() << !DesignatorStr.empty() + << DesignatorStr << CountExpr->getSourceRange(); + } - TypeSourceInfo *TInfo; - QualType literalType = GetTypeFromParser(Ty, &TInfo); - if (!TInfo) - TInfo = Context.getTrivialTypeSourceInfo(literalType); + // The __counted_by_or_null()/__sized_by_or_null() pointer is set to some + // unknown value with a negative count/size. Emit a warning, since this is + // likely a mistake. + if (CountVal < 0 && !IsNull && IsOrNull) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::warn_bounds_safety_dynamic_count_from_nonnull_negative_count) + << Action << LHSTy << LDCPTy->isCountInBytes() + << CountVal.getSExtValue() << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + } - return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr); + return true; } -ExprResult -Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, - SourceLocation RParenLoc, Expr *LiteralExpr) { - QualType literalType = TInfo->getType(); +} // namespace - if (literalType->isArrayType()) { - if (RequireCompleteSizedType( - LParenLoc, Context.getBaseElementType(literalType), - diag::err_array_incomplete_or_sizeless_type, - SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) - return ExprError(); - if (literalType->isVariableArrayType()) { - // C23 6.7.10p4: An entity of variable length array type shall not be - // initialized except by an empty initializer. - // - // The C extension warnings are issued from ParseBraceInitializer() and - // do not need to be issued here. However, we continue to issue an error - // in the case there are initializers or we are compiling C++. We allow - // use of VLAs in C++, but it's not clear we want to allow {} to zero - // init a VLA in C++ in all cases (such as with non-trivial constructors). - // FIXME: should we allow this construct in C++ when it makes sense to do - // so? - // - // But: C99-C23 6.5.2.5 Compound literals constraint 1: The type name - // shall specify an object type or an array of unknown size, but not a - // variable length array type. This seems odd, as it allows 'int a[size] = - // {}', but forbids 'int *a = (int[size]){}'. As this is what the standard - // says, this is what's implemented here for C (except for the extension - // that permits constant foldable size arrays) +// Check dynamic count pointer assignment constrains. +// +// RHSExpr is the RHS of the assignment to the dynamic count pointer. RHSExpr +// can be an ImplicitValueInitExpr if the dynamic count pointer is implicitly +// initialized to null. +// +// Loc is the location where the diagnostic should be emitted. It points to the +// assign op for assignments, and dynamic count pointer argument for function +// calls. For initialization, it might point to the initializer or the '}' in +// the parent initializer if the initialization is implicit. +// +// DependentValues denotes the values assigned to dependent variables used in +// the dynamic count pointer's count expression. If the values cannot be +// inferred from the context, the map can be left empty, but the warnings will +// be less precise. +bool Sema::CheckDynamicCountSizeForAssignment( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, + DependentValuesMap &DependentValues, Expr *LHSMemberBase) { + const auto *LDCPTy = LHSTy->getAs(); + // RHS may be a function or an array type. + // Do not diagnose here. It must be the second time to visit this conversion. + ExprResult RHS = DefaultFunctionArrayLvalueConversion( + RHSExpr, /*Diagnose*/ false, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion*/ false); + if (RHS.isInvalid()) + return false; + RHSExpr = RHS.get(); - auto diagID = LangOpts.CPlusPlus - ? diag::err_variable_object_no_init - : diag::err_compound_literal_with_vla_type; - if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, - diagID)) - return ExprError(); + QualType RHSTy = RHSExpr->getType(); + if (!LDCPTy) + return true; + + Expr *CountExpr = LDCPTy->getCountExpr()->IgnoreParenCasts(); + if (CountExpr->isValueDependent()) + return false; + + auto *RHSPTy = RHSTy->getAs(); + auto FA = + RHSPTy ? RHSPTy->getPointerAttributes() : BoundsSafetyPointerAttributes(); + // unsafe/unspecified counts as "bounded" because there shouldn't be a warning + // in manual adoption mode, else no one would be able to use it. In regular + // -fbounds-safety mode, some other part of Sema should have already have + // complained. + const auto *RDRPTy = RHSTy->getAs(); + bool UnboundedRHS = !RHSTy->isCountAttributedType() && + (!RDRPTy || !RDRPTy->getEndPointer()) && + !FA.hasUpperBound() && !FA.isUnsafeOrUnspecified(); + if (UnboundedRHS) + // It might still have a flexible array member + if (auto *RT = RHSTy->getPointeeType()->getAs()) + UnboundedRHS = !RT->getDecl()->hasFlexibleArrayMember(); + + ReplaceDeclRefWithRHS Transform(*this, DependentValues); + if (!DependentValues.empty()) { + if (LHSMemberBase) + Transform.MemberBase = LHSMemberBase; + ExprResult CountExprRes = Transform.TransformBoundsAttrExpr(CountExpr); + if (CountExprRes.isInvalid()) + return false; + CountExpr = CountExprRes.get(); + + // The count expression might be valid, but the new value assigned may not + // be, so check again after transforming + if (CountExpr->isValueDependent()) + return false; + } + const auto &ReplacingValues = Transform.GetReplacingValues(); + + Expr::EvalResult Res; + if (CountExpr->EvaluateAsInt(Res, Context)) { + const llvm::APSInt &CountVal = Res.Val.getInt(); + return checkDynamicCountSizeForAssignmentWithKnownCount( + *this, LHSTy, RHSExpr, Action, Loc, Designator, DependentValues, + UnboundedRHS, CountExpr, CountVal, ReplacingValues); + } + + return checkDynamicCountSizeForAssignmentWithUnknownCount( + *this, LHSTy, RHSExpr, Action, Loc, Designator, UnboundedRHS, CountExpr, + ReplacingValues); +} + +enum class DAIKind { START_PTR, END_PTR, COUNT_PTR, COUNT }; + +// Information of declarations dependent on function argument or parameter. +struct DepArgInfo { + const ValueDecl *VD; + DAIKind Kind; + // Argument or Parameter index + unsigned Index; + // For COUNT_PTR/OUT_START `IsDeref` is the nested level at which the count + // attribute is added. + // In the following example, `IsDeref` is `true` for `out_buf` as `VD`. + // `void f(int *__counted_by(*out_len) *out_buf, int *out_len)` + // For COUNT/END_PTR, this is whether the value is dereferenced + // in the expression of the bounds attribute. Since `out_len` is dereferenced + // in `__counted_by` above, `IsDeref` is true for `out_len`. + bool IsDeref; + // This indicates a decl that has dependency with an argument/param but is not + // actually used for the function decl or the function call. + bool Unlisted; + + explicit DepArgInfo(const ValueDecl *VD, DAIKind Kind, unsigned Index, + bool IsDeref, bool Unlisted) + : VD(VD), Kind(Kind), Index(Index), IsDeref(IsDeref), Unlisted(Unlisted) { + } + + bool matches(const DepArgInfo &Other) const { + return Kind == Other.Kind && Index == Other.Index && + Unlisted == Other.Unlisted; + } + + const ValueDecl *getDecl() const { return VD; } + + bool isDeref() const { return IsDeref; } + bool isParam() const { return VD && isa(VD); } + bool isCountPointer() const { return Kind == DAIKind::COUNT_PTR; } + const CountAttributedType *getCountAttributedType() const { + if (!isCountPointer()) + return nullptr; + QualType Ty = getDecl()->getType(); + if (IsDeref) { + Ty = Ty->getPointeeType(); } - } else if (!literalType->isDependentType() && - RequireCompleteType(LParenLoc, literalType, - diag::err_typecheck_decl_incomplete_type, - SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) - return ExprError(); + return Ty->getAs(); + } +}; - InitializedEntity Entity - = InitializedEntity::InitializeCompoundLiteralInit(TInfo); - InitializationKind Kind - = InitializationKind::CreateCStyleCast(LParenLoc, - SourceRange(LParenLoc, RParenLoc), - /*InitList=*/true); - InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr); - ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr, - &literalType); - if (Result.isInvalid()) - return ExprError(); - LiteralExpr = Result.get(); +// Argument/parameter information necessary for out parameter analysis. +class DynamicBoundArgumentInfo { - bool isFileScope = !CurContext->isFunctionOrMethod(); + const BoundsAttributedType *DBPTy; + const ValueDecl *BaseDecl; - // In C, compound literals are l-values for some reason. - // For GCC compatibility, in C++, file-scope array compound literals with - // constant initializers are also l-values, and compound literals are - // otherwise prvalues. + unsigned Level; + bool IsAddrOf : 1; + + // IsCount is true iff there exists a pointer param that depends on this + // count. IsCountInRet/IsEndInRet is true iff the return type depends on this + // count/end. Example values for the len param: + // void f(int *__counted_by(len) p, int len): + // IsCountInParam=1 IsCountInRet=0 // - // (GCC also treats C++ list-initialized file-scope array prvalues with - // constant initializers as l-values, but that's non-conforming, so we don't - // follow it there.) + // int *__counted_by(len) f(int len): + // IsCountInParam=0 IsCountInRet=1 // - // FIXME: It would be better to handle the lvalue cases as materializing and - // lifetime-extending a temporary object, but our materialized temporaries - // representation only supports lifetime extension from a variable, not "out - // of thin air". - // FIXME: For C++, we might want to instead lifetime-extend only if a pointer - // is bound to the result of applying array-to-pointer decay to the compound - // literal. - // FIXME: GCC supports compound literals of reference type, which should - // obviously have a value kind derived from the kind of reference involved. - ExprValueKind VK = - (getLangOpts().CPlusPlus && !(isFileScope && literalType->isArrayType())) - ? VK_PRValue - : VK_LValue; + // int *__counted_by(len) f(int *__counted_by(len) p, int len): + // IsCountInParam=1 IsCountInRet=1 + bool IsCountInParam : 1; + bool IsCountInRet : 1; + bool IsEndInRet : 1; - if (isFileScope) - if (auto ILE = dyn_cast(LiteralExpr)) - for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) { - Expr *Init = ILE->getInit(i); - ILE->setInit(i, ConstantExpr::Create(Context, Init)); - } + // DepArgInfos for call arguments are produced after the constructor. This is + // to keep this state. + bool HasValidDepInfo : 1; + SmallVector DepArgInfos; - auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, - VK, LiteralExpr, isFileScope); - if (isFileScope) { - if (!LiteralExpr->isTypeDependent() && - !LiteralExpr->isValueDependent() && - !literalType->isDependentType()) // C99 6.5.2.5p3 - if (CheckForConstantInitializer(LiteralExpr)) - return ExprError(); - } else if (literalType.getAddressSpace() != LangAS::opencl_private && - literalType.getAddressSpace() != LangAS::Default) { - // Embedded-C extensions to C99 6.5.2.5: - // "If the compound literal occurs inside the body of a function, the - // type name shall not be qualified by an address-space qualifier." - Diag(LParenLoc, diag::err_compound_literal_with_address_space) - << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()); - return ExprError(); + void processDependentParamOfReturnType() { + IsCountInRet = false; + IsEndInRet = false; + + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + if (BaseDecl && BaseDecl->isDependentParamOfReturnType(&RetType, &Info)) { + bool IsCAT = isa(RetType); + IsCountInRet = IsCAT; + IsEndInRet = !IsCAT; + assert(!IsEndInRet || isa(RetType)); + assert(!IsCountInParam || Level == Info->isDeref()); + Level = Info->isDeref(); + } } - if (!isFileScope && !getLangOpts().CPlusPlus) { - // Compound literals that have automatic storage duration are destroyed at - // the end of the scope in C; in C++, they're just temporaries. +public: + using DeclIndexMapTy = + llvm::DenseMap>; + + DynamicBoundArgumentInfo(ASTContext &Ctx, Expr *Arg) + : Level(0), HasValidDepInfo(false) { + Expr *E = Arg->IgnoreImpCasts(); + QualType ArgTy = E->getType(); + + bool LocalIsAddrOf = false; + bool LocalIsDeref = false; + BaseDecl = findValueDecl(E, &LocalIsAddrOf, &LocalIsDeref); + IsAddrOf = LocalIsAddrOf; + + DBPTy = ArgTy->getAs(); + if (!DBPTy && ArgTy->isPointerType() && + (DBPTy = ArgTy->getPointeeType()->getAs())) { + Level++; + } + if (BaseDecl && BaseDecl->hasAttr()) { + IsCountInParam = true; + if (BaseDecl->getType()->isPointerType() && !LocalIsDeref) + Level = 1; + } else { + IsCountInParam = false; + } - // Emit diagnostics if it is or contains a C union type that is non-trivial - // to destruct. - if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) - checkNonTrivialCUnion(E->getType(), E->getExprLoc(), - NTCUC_CompoundLiteral, NTCUK_Destruct); + processDependentParamOfReturnType(); - // Diagnose jumps that enter or exit the lifetime of the compound literal. - if (literalType.isDestructedType()) { - Cleanup.setExprNeedsCleanups(true); - ExprCleanupObjects.push_back(E); - getCurFunction()->setHasBranchProtectedScope(); - } + // Fill out DepArgInfos later via processDepArgInfos() after + // the declaration to argument index map has been produced. } - if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || - E->getType().hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnionInInitializer(E->getInitializer(), - E->getInitializer()->getExprLoc()); + DynamicBoundArgumentInfo(const ParmVarDecl *BaseDecl) + : BaseDecl(BaseDecl), Level(0), IsAddrOf(false), HasValidDepInfo(true) { + QualType ParmTy = BaseDecl->getType(); + DBPTy = ParmTy->getAs(); + if (!DBPTy && ParmTy->isPointerType() && + (DBPTy = ParmTy->getPointeeType()->getAs())) { + Level++; + } - return MaybeBindToTemporary(E); -} + if (BaseDecl->hasAttr()) { + IsCountInParam = true; + if (BaseDecl->getType()->isPointerType()) + Level = 1; + } else { + IsCountInParam = false; + } -ExprResult -Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, - SourceLocation RBraceLoc) { - // Only produce each kind of designated initialization diagnostic once. - SourceLocation FirstDesignator; - bool DiagnosedArrayDesignator = false; - bool DiagnosedNestedDesignator = false; - bool DiagnosedMixedDesignator = false; + processDependentParamOfReturnType(); - // Check that any designated initializers are syntactically valid in the - // current language mode. - for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { - if (auto *DIE = dyn_cast(InitArgList[I])) { - if (FirstDesignator.isInvalid()) - FirstDesignator = DIE->getBeginLoc(); + auto processDepInfo = [&](const Decl *D, bool IsDeref, DAIKind Kind) { + const auto *VD = cast(D); + unsigned Index = 0; + bool Unlisted = false; + if (const auto *PVD = dyn_cast(VD)) { + Index = PVD->getFunctionScopeIndex(); + } else { + Unlisted = true; + } - if (!getLangOpts().CPlusPlus) - break; + DepArgInfos.emplace_back(VD, Kind, Index, IsDeref, Unlisted); + }; - if (!DiagnosedNestedDesignator && DIE->size() > 1) { - DiagnosedNestedDesignator = true; - Diag(DIE->getBeginLoc(), diag::ext_designated_init_nested) - << DIE->getDesignatorsSourceRange(); + if (isCountInParam()) { + if (const auto *Att = BaseDecl->getAttr()) { + processCountDepInfo(Att, processDepInfo); } + return; + } - for (auto &Desig : DIE->designators()) { - if (!Desig.isFieldDesignator() && !DiagnosedArrayDesignator) { - DiagnosedArrayDesignator = true; - Diag(Desig.getBeginLoc(), diag::ext_designated_init_array) - << Desig.getSourceRange(); - } - } + if (!DBPTy) + return; - if (!DiagnosedMixedDesignator && - !isa(InitArgList[0])) { - DiagnosedMixedDesignator = true; - Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) - << DIE->getSourceRange(); - Diag(InitArgList[0]->getBeginLoc(), diag::note_designated_init_mixed) - << InitArgList[0]->getSourceRange(); + if (const auto *DCPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DCPTy->dependent_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::COUNT); } - } else if (getLangOpts().CPlusPlus && !DiagnosedMixedDesignator && - isa(InitArgList[0])) { - DiagnosedMixedDesignator = true; - auto *DIE = cast(InitArgList[0]); - Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) - << DIE->getSourceRange(); - Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed) - << InitArgList[I]->getSourceRange(); + return; } - } - if (FirstDesignator.isValid()) { - // Only diagnose designated initiaization as a C++20 extension if we didn't - // already diagnose use of (non-C++20) C99 designator syntax. - if (getLangOpts().CPlusPlus && !DiagnosedArrayDesignator && - !DiagnosedNestedDesignator && !DiagnosedMixedDesignator) { - Diag(FirstDesignator, getLangOpts().CPlusPlus20 - ? diag::warn_cxx17_compat_designated_init - : diag::ext_cxx_designated_init); - } else if (!getLangOpts().CPlusPlus && !getLangOpts().C99) { - Diag(FirstDesignator, diag::ext_designated_init); + if (const auto *DRPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DRPTy->startptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::START_PTR); + } + for (const auto &DI : DRPTy->endptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::END_PTR); + } + return; } } - return BuildInitList(LBraceLoc, InitArgList, RBraceLoc); -} + const SmallVectorImpl &getDepArgInfos() const { + return DepArgInfos; + } -ExprResult -Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, - SourceLocation RBraceLoc) { - // Semantic analysis for initializers is done by ActOnDeclarator() and - // CheckInitializer() - it requires knowledge of the object being initialized. + SmallVectorImpl::iterator depinfo_begin() { + return DepArgInfos.begin(); + } - // Immediately handle non-overload placeholders. Overloads can be - // resolved contextually, but everything else here can't. - for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { - if (InitArgList[I]->getType()->isNonOverloadPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(InitArgList[I]); + SmallVectorImpl::iterator depinfo_end() { + return DepArgInfos.end(); + } - // Ignore failures; dropping the entire initializer list because - // of one failure would be terrible for indexing/etc. - if (result.isInvalid()) continue; + SmallVectorImpl::const_iterator depinfo_begin() const { + return DepArgInfos.begin(); + } - InitArgList[I] = result.get(); - } + SmallVectorImpl::const_iterator depinfo_end() const { + return DepArgInfos.end(); } - InitListExpr *E = - new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); - E->setType(Context.VoidTy); // FIXME: just a place holder for now. - return E; -} + unsigned getLevel() const { return Level; } -void Sema::maybeExtendBlockObject(ExprResult &E) { - assert(E.get()->getType()->isBlockPointerType()); - assert(E.get()->isPRValue()); + bool isBoundsAttributedType() const { return !!DBPTy; } - // Only do this in an r-value context. - if (!getLangOpts().ObjCAutoRefCount) return; + bool isDynamicRangePointerType() const { + return DBPTy && isa(DBPTy); + } - E = ImplicitCastExpr::Create( - Context, E.get()->getType(), CK_ARCExtendBlockObject, E.get(), - /*base path*/ nullptr, VK_PRValue, FPOptionsOverride()); - Cleanup.setExprNeedsCleanups(true); -} + bool isCountInParam() const { return IsCountInParam; } -CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) { - // Both Src and Dest are scalar types, i.e. arithmetic or pointer. - // Also, callers should have filtered out the invalid cases with - // pointers. Everything else should be possible. + bool isCountInRet() const { return IsCountInRet; } - QualType SrcTy = Src.get()->getType(); - if (Context.hasSameUnqualifiedType(SrcTy, DestTy)) - return CK_NoOp; + bool isEndInRet() const { return IsEndInRet; } - switch (Type::ScalarTypeKind SrcKind = SrcTy->getScalarTypeKind()) { - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - - case Type::STK_CPointer: - case Type::STK_BlockPointer: - case Type::STK_ObjCObjectPointer: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_CPointer: { - LangAS SrcAS = SrcTy->getPointeeType().getAddressSpace(); - LangAS DestAS = DestTy->getPointeeType().getAddressSpace(); - if (SrcAS != DestAS) - return CK_AddressSpaceConversion; - if (Context.hasCvrSimilarType(SrcTy, DestTy)) - return CK_NoOp; - return CK_BitCast; - } - case Type::STK_BlockPointer: - return (SrcKind == Type::STK_BlockPointer - ? CK_BitCast : CK_AnyPointerToBlockPointerCast); - case Type::STK_ObjCObjectPointer: - if (SrcKind == Type::STK_ObjCObjectPointer) - return CK_BitCast; - if (SrcKind == Type::STK_CPointer) - return CK_CPointerToObjCPointerCast; - maybeExtendBlockObject(Src); - return CK_BlockPointerToObjCPointerCast; - case Type::STK_Bool: - return CK_PointerToBoolean; - case Type::STK_Integral: - return CK_PointerToIntegral; - case Type::STK_Floating: - case Type::STK_FloatingComplex: - case Type::STK_IntegralComplex: - case Type::STK_MemberPointer: - case Type::STK_FixedPoint: - llvm_unreachable("illegal cast from pointer"); - } - llvm_unreachable("Should have returned before this"); + bool isCountInParamOrCountPointer() const { + return IsCountInParam || isCountAttributedType(); + } - case Type::STK_FixedPoint: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FixedPoint: - return CK_FixedPointCast; - case Type::STK_Bool: - return CK_FixedPointToBoolean; - case Type::STK_Integral: - return CK_FixedPointToIntegral; - case Type::STK_Floating: - return CK_FixedPointToFloating; - case Type::STK_IntegralComplex: - case Type::STK_FloatingComplex: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << DestTy; - return CK_IntegralCast; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - case Type::STK_MemberPointer: - llvm_unreachable("illegal cast to pointer type"); - } - llvm_unreachable("Should have returned before this"); + bool isCountAttributedType() const { + return DBPTy && isa(DBPTy); + } - case Type::STK_Bool: // casting from bool is like casting from an integer - case Type::STK_Integral: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - if (Src.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) - return CK_NullToPointer; - return CK_IntegralToPointer; - case Type::STK_Bool: - return CK_IntegralToBoolean; - case Type::STK_Integral: - return CK_IntegralCast; - case Type::STK_Floating: - return CK_IntegralToFloating; - case Type::STK_IntegralComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_IntegralCast); - return CK_IntegralRealToComplex; - case Type::STK_FloatingComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_IntegralToFloating); - return CK_FloatingRealToComplex; - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - return CK_IntegralToFixedPoint; - } - llvm_unreachable("Should have returned before this"); + const CountAttributedType *getCountAttributedType() const { + return dyn_cast_or_null(DBPTy); + } - case Type::STK_Floating: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_Floating: - return CK_FloatingCast; - case Type::STK_Bool: - return CK_FloatingToBoolean; - case Type::STK_Integral: - return CK_FloatingToIntegral; - case Type::STK_FloatingComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_FloatingCast); - return CK_FloatingRealToComplex; - case Type::STK_IntegralComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_FloatingToIntegral); - return CK_IntegralRealToComplex; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid float->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - return CK_FloatingToFixedPoint; - } - llvm_unreachable("Should have returned before this"); + bool isOutParameter() const { return Level != 0 || IsAddrOf; } - case Type::STK_FloatingComplex: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FloatingComplex: - return CK_FloatingComplexCast; - case Type::STK_IntegralComplex: - return CK_FloatingComplexToIntegralComplex; - case Type::STK_Floating: { - QualType ET = SrcTy->castAs()->getElementType(); - if (Context.hasSameType(ET, DestTy)) - return CK_FloatingComplexToReal; - Src = ImpCastExprToType(Src.get(), ET, CK_FloatingComplexToReal); - return CK_FloatingCast; - } - case Type::STK_Bool: - return CK_FloatingComplexToBoolean; - case Type::STK_Integral: - Src = ImpCastExprToType(Src.get(), - SrcTy->castAs()->getElementType(), - CK_FloatingComplexToReal); - return CK_FloatingToIntegral; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid complex float->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << SrcTy; - return CK_IntegralCast; - } - llvm_unreachable("Should have returned before this"); + bool isOutRangePointer() const { + return isDynamicRangePointerType() && isOutParameter(); + } - case Type::STK_IntegralComplex: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FloatingComplex: - return CK_IntegralComplexToFloatingComplex; - case Type::STK_IntegralComplex: - return CK_IntegralComplexCast; - case Type::STK_Integral: { - QualType ET = SrcTy->castAs()->getElementType(); - if (Context.hasSameType(ET, DestTy)) - return CK_IntegralComplexToReal; - Src = ImpCastExprToType(Src.get(), ET, CK_IntegralComplexToReal); - return CK_IntegralCast; - } - case Type::STK_Bool: - return CK_IntegralComplexToBoolean; - case Type::STK_Floating: - Src = ImpCastExprToType(Src.get(), - SrcTy->castAs()->getElementType(), - CK_IntegralComplexToReal); - return CK_IntegralToFloating; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid complex int->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << SrcTy; - return CK_IntegralCast; - } - llvm_unreachable("Should have returned before this"); + bool isOutCountPointer() const { + return isCountAttributedType() && isOutParameter(); } - llvm_unreachable("Unhandled scalar cast"); -} + bool isIncompleteArray() const { return DBPTy->isIncompleteArrayType(); } -static bool breakDownVectorType(QualType type, uint64_t &len, - QualType &eltType) { - // Vectors are simple. - if (const VectorType *vecType = type->getAs()) { - len = vecType->getNumElements(); - eltType = vecType->getElementType(); - assert(eltType->isScalarType()); - return true; + bool isInOutCountPointer() const { + if (!isCountAttributedType() || isOutParameter()) + return false; + return std::any_of(depinfo_begin(), depinfo_end(), + [](auto &DepInfo) { return DepInfo.isDeref(); }); } - // We allow lax conversion to and from non-vector types, but only if - // they're real types (i.e. non-complex, non-pointer scalar types). - if (!type->isRealType()) return false; - - len = 1; - eltType = type; - return true; -} + bool isOutCountInParam() const { return IsCountInParam && isOutParameter(); } -bool Sema::isValidSveBitcast(QualType srcTy, QualType destTy) { - assert(srcTy->isVectorType() || destTy->isVectorType()); + bool isOutCountInParamOrPointer() const { + return isOutCountInParam() || isOutCountPointer(); + } - auto ValidScalableConversion = [](QualType FirstType, QualType SecondType) { - if (!FirstType->isSVESizelessBuiltinType()) - return false; + bool isParmDecl() const { return BaseDecl && isa(BaseDecl); } - const auto *VecTy = SecondType->getAs(); - return VecTy && VecTy->getVectorKind() == VectorKind::SveFixedLengthData; - }; + bool isAddrOf() const { return IsAddrOf; } - return ValidScalableConversion(srcTy, destTy) || - ValidScalableConversion(destTy, srcTy); -} + bool isAddrOfInBufParam() const { + return isBoundsAttributedType() && isParmDecl() && isAddrOf() && + Level == 1; + } -bool Sema::areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy) { - if (!destTy->isMatrixType() || !srcTy->isMatrixType()) - return false; + QualType getType() const { return QualType(DBPTy, 0); } - const ConstantMatrixType *matSrcType = srcTy->getAs(); - const ConstantMatrixType *matDestType = destTy->getAs(); + const ValueDecl *getDecl() const { return BaseDecl; } - return matSrcType->getNumRows() == matDestType->getNumRows() && - matSrcType->getNumColumns() == matDestType->getNumColumns(); -} + void processCountDepInfo( + const DependerDeclsAttr *Att, + const std::function &processDepInfo) { + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const auto *D = Att->dependerDecls_begin()[i]; + unsigned DependerLevel = Att->dependerLevels_begin()[i]; + assert(DependerLevel < 2 && + "External bounds attributes can only be added to" + "a pointer which is one-level nested at most"); + bool IsDeref = DependerLevel; + processDepInfo(D, IsDeref, DAIKind::COUNT_PTR); + } + } -bool Sema::areVectorTypesSameSize(QualType SrcTy, QualType DestTy) { - assert(DestTy->isVectorType() || SrcTy->isVectorType()); + /// \p DeclIndexMap is a map from a decl used in an argument expression to the + /// call argument index. + void processDepArgInfos(const DeclIndexMapTy &DeclIndexMap) { + HasValidDepInfo = true; - uint64_t SrcLen, DestLen; - QualType SrcEltTy, DestEltTy; - if (!breakDownVectorType(SrcTy, SrcLen, SrcEltTy)) - return false; - if (!breakDownVectorType(DestTy, DestLen, DestEltTy)) - return false; + auto processDepInfo = [&](const Decl *D, bool IsDeref, DAIKind Kind) { + const auto *VD = cast(D); + auto I = DeclIndexMap.find(VD); + if (I == DeclIndexMap.end()) { + DepArgInfos.emplace_back(VD, Kind, 0, IsDeref, /*Unlisted*/true); + return; + } + for (auto Index : I->second) { + DepArgInfos.emplace_back(VD, Kind, Index, IsDeref, /*Unlisted*/false); + } + }; - // ASTContext::getTypeSize will return the size rounded up to a - // power of 2, so instead of using that, we need to use the raw - // element size multiplied by the element count. - uint64_t SrcEltSize = Context.getTypeSize(SrcEltTy); - uint64_t DestEltSize = Context.getTypeSize(DestEltTy); + if (isCountInParam()) { + if (const auto *Att = getDecl()->getAttr()) { + processCountDepInfo(Att, processDepInfo); + } + return; + } - return (SrcLen * SrcEltSize == DestLen * DestEltSize); -} + if (!DBPTy) + return; -bool Sema::anyAltivecTypes(QualType SrcTy, QualType DestTy) { - assert((DestTy->isVectorType() || SrcTy->isVectorType()) && - "expected at least one type to be a vector here"); + if (const auto *DCPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DCPTy->dependent_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::COUNT); + } + return; + } - bool IsSrcTyAltivec = - SrcTy->isVectorType() && ((SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecVector) || - (SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecBool) || - (SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecPixel)); + if (const auto *DRPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DRPTy->startptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::START_PTR); + } + for (const auto &DI : DRPTy->endptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::END_PTR); + } + return; + } + } +}; - bool IsDestTyAltivec = DestTy->isVectorType() && - ((DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecVector) || - (DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecBool) || - (DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecPixel)); +static bool checkDynamicCountPointerAsParameter(Sema &S, FunctionDecl *FDecl, + CallExpr *Call) { + if (Call->containsErrors()) + return false; + // Initialize argument/parameter information + llvm::SmallPtrSet OutCountDecls; + using PointerOutPair = llvm::PointerIntPair; + llvm::SmallPtrSet PointerDecls; - return (IsSrcTyAltivec || IsDestTyAltivec); -} + llvm::SmallVector CallArgInfos; + DynamicBoundArgumentInfo::DeclIndexMapTy DeclIndexMap; -bool Sema::areLaxCompatibleVectorTypes(QualType srcTy, QualType destTy) { - assert(destTy->isVectorType() || srcTy->isVectorType()); + for (unsigned i = 0; i < Call->getNumArgs(); ++i) { + CallArgInfos.emplace_back(S.Context, Call->getArg(i)); + if (auto *D = CallArgInfos.back().getDecl()) + DeclIndexMap[D].push_back(i); + } - // Disallow lax conversions between scalars and ExtVectors (these - // conversions are allowed for other vector types because common headers - // depend on them). Most scalar OP ExtVector cases are handled by the - // splat path anyway, which does what we want (convert, not bitcast). - // What this rules out for ExtVectors is crazy things like char4*float. - if (srcTy->isScalarType() && destTy->isExtVectorType()) return false; - if (destTy->isScalarType() && srcTy->isExtVectorType()) return false; + for (auto &ArgInfo : CallArgInfos) { + ArgInfo.processDepArgInfos(DeclIndexMap); + } - return areVectorTypesSameSize(srcTy, destTy); -} + llvm::SmallVector FuncParmInfos; + for (unsigned i = 0; i < FDecl->getNumParams(); ++i) { + FuncParmInfos.emplace_back(FDecl->getParamDecl(i)); + } -bool Sema::isLaxVectorConversion(QualType srcTy, QualType destTy) { - assert(destTy->isVectorType() || srcTy->isVectorType()); + unsigned MinNumArgs = std::min(Call->getNumArgs(), FDecl->getNumParams()); - switch (Context.getLangOpts().getLaxVectorConversions()) { - case LangOptions::LaxVectorConversionKind::None: - return false; + Sema::DependentValuesMap DependentValues; + for (unsigned i = 0; i < MinNumArgs; ++i) { + const ParmVarDecl *D = FDecl->getParamDecl(i); + Expr *Arg = Call->getArg(i); + DependentValues[D] = {Arg, /*Level=*/0}; + } - case LangOptions::LaxVectorConversionKind::Integer: - if (!srcTy->isIntegralOrEnumerationType()) { - auto *Vec = srcTy->getAs(); - if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) - return false; + // FIXME: Report an error when a pointer to dynamic count or a pointer to + // count pointer is passed as va_arg (rdar://97041755). + for (unsigned i = 0; i < MinNumArgs; ++i) { + const auto &ArgInfo = CallArgInfos[i]; + const auto &ParmInfo = FuncParmInfos[i]; + Expr *ActualArgExp = Call->getArg(i)->IgnoreImpCasts(); + SourceLocation ArgLoc = ActualArgExp->getExprLoc(); + + if (!ArgInfo.isCountInParamOrCountPointer() && !ArgInfo.isCountInRet() && + !ParmInfo.isCountInParamOrCountPointer() && !ParmInfo.isCountInRet()) + continue; + assert(!ActualArgExp->isValueDependent()); + // Disable these checks for attribute-only mode because we don't want + // non-type-incompatibility errors in that mode. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + ParmInfo.isCountAttributedType() && !ParmInfo.isOutCountPointer() && + !S.CheckDynamicCountSizeForAssignment( + ParmInfo.getType(), ActualArgExp, Sema::AA_Passing, + ActualArgExp->getBeginLoc(), ParmInfo.getDecl()->getName(), + DependentValues) && + !S.allowBoundsUnsafeFunctionArg(Call, i)) { + return false; } - if (!destTy->isIntegralOrEnumerationType()) { - auto *Vec = destTy->getAs(); - if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + + if (ParmInfo.isOutCountPointer() && !ArgInfo.isOutCountPointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } + + if (!ParmInfo.isOutCountPointer() && ArgInfo.isOutCountPointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } + + // TODO: This diagnostic check should always be performed + // (rdar://138982703). The check is currently guarded to avoid potentially + // breaking the build. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + ArgInfo.isCountInRet() && S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + bool IsIndirect = ActualArgExp->getType()->isPointerType(); + bool IsOutCountInRetToOutCountInRet = + ArgInfo.getLevel() != 0 && ParmInfo.isCountInRet() && + ParmInfo.getLevel() != 0 && !ParmInfo.isOutCountInParam(); + bool IsOutCountInRetToInoutCount = + ArgInfo.getLevel() != 0 && ParmInfo.isOutCountInParam() && + std::none_of(ParmInfo.depinfo_begin(), ParmInfo.depinfo_end(), + [](const DepArgInfo &ParmDepInfo) { + return ParmDepInfo.isDeref(); + }); + if (!(!IsIndirect || IsOutCountInRetToOutCountInRet || + IsOutCountInRetToInoutCount)) { + const auto *ArgPVD = cast(ArgInfo.getDecl()); + const auto *Caller = cast(ArgPVD->getDeclContext()); + const auto *RetCATy = + Caller->getReturnType()->getAs(); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dependent_param_in_return) + << ArgPVD->getName() << RetCATy->getKind() << Caller->getName() + << Caller->getReturnType(); return false; + } } - // OK, integer (vector) -> integer (vector) bitcast. - break; - case LangOptions::LaxVectorConversionKind::All: - break; - } + if (ParmInfo.isOutCountInParam() && !ArgInfo.isOutCountInParam()) { + if (std::none_of(ParmInfo.depinfo_begin(), ParmInfo.depinfo_end(), + [](const DepArgInfo &ParmDepInfo) { + return ParmDepInfo.isDeref(); })) { + continue; + } + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } - return areLaxCompatibleVectorTypes(srcTy, destTy); -} + if (!ParmInfo.isOutCountInParam() && ArgInfo.isOutCountInParam()) { + const CountAttributedType *DCPTy = nullptr; + for (const auto &ArgDepInfo : ArgInfo.getDepArgInfos()) { + if (ArgDepInfo.isCountPointer()) { + DCPTy = ArgDepInfo.getCountAttributedType(); + break; + } + } + assert(DCPTy); + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_indirect_count_parameter) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << DCPTy->getKind() + << Call->getSourceRange(); + return false; + } -bool Sema::CheckMatrixCast(SourceRange R, QualType DestTy, QualType SrcTy, - CastKind &Kind) { - if (SrcTy->isMatrixType() && DestTy->isMatrixType()) { - if (!areMatrixTypesOfTheSameDimension(SrcTy, DestTy)) { - return Diag(R.getBegin(), diag::err_invalid_conversion_between_matrixes) - << DestTy << SrcTy << R; + if (ArgInfo.isAddrOfInBufParam()) { + // This is to handle the code like below: + // void foo(int *__counted_by(*len) buf, size_t *len) { + // bar(&buf, len); + // } + // `buf` is read-only so it should not be passed as an out parameter. + for (const auto &DI : ArgInfo.getDepArgInfos()) { + if (DI.Unlisted) + continue; + const auto &CallArgInfo = CallArgInfos[DI.Index]; + if (CallArgInfo.isOutCountInParam() && CallArgInfo.isParmDecl()) { + const auto *DCPTy = ArgInfo.getCountAttributedType(); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer) + << ArgInfo.getDecl()->getName() << DCPTy->getKind(); + return false; + } + } } - } else if (SrcTy->isMatrixType()) { - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_matrix_and_type) - << SrcTy << DestTy << R; - } else if (DestTy->isMatrixType()) { - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_matrix_and_type) - << DestTy << SrcTy << R; - } - Kind = CK_MatrixCast; - return false; -} + auto reportIncompatibleCountExprs = [&]() { + const auto *ParmDCPTy = ParmInfo.getCountAttributedType(); + const auto *ArgDCPTy = ArgInfo.getCountAttributedType(); + if (ParmDCPTy->isCountInBytes() != ArgDCPTy->isCountInBytes() || + !compatibleDynamicCountExprs(S.Context, ParmDCPTy->getCountExpr(), + ArgDCPTy->getCountExpr())) { + S.Diag(ActualArgExp->getExprLoc(), + diag::err_bounds_safety_incompatible_count_expression) + << ParmDCPTy->getCountExpr() << ArgDCPTy->getCountExpr(); + } + }; -bool Sema::CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, - CastKind &Kind) { - assert(VectorTy->isVectorType() && "Not a vector type!"); + // check parameter relationship! + if (ParmInfo.isOutCountPointer()) { + assert(ArgInfo.isOutCountPointer()); + reportIncompatibleCountExprs(); + } + + // Should check compatibility of count expressions as well if this is a + // direct parameter using out count. + if (ParmInfo.isInOutCountPointer() && + ArgInfo.getCountAttributedType()) { + reportIncompatibleCountExprs(); + } + + if ((ParmInfo.isOutCountPointer() && !ParmInfo.isIncompleteArray()) || + ParmInfo.isOutCountInParam()) { + // For attribute only mode, we do not want to report + // 'err_bounds_safety_unsynchronized_indirect_param'. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode()) { + for (const auto &ArgDepInfo : ArgInfo.getDepArgInfos()) { + if (ArgDepInfo.Unlisted || + (ArgInfo.isOutCountPointer() && + !CallArgInfos[ArgDepInfo.Index].isOutCountInParam())) { + unsigned IsDependent = 0; + + QualType DiagTy = ArgInfo.getType(); + if (ArgInfo.isCountInParam()) { + IsDependent = 1; + DiagTy = ArgDepInfo.getDecl()->getType(); + } - if (Ty->isVectorType() || Ty->isIntegralType(Context)) { - if (!areLaxCompatibleVectorTypes(Ty, VectorTy)) - return Diag(R.getBegin(), - Ty->isVectorType() ? - diag::err_invalid_conversion_between_vectors : - diag::err_invalid_conversion_between_vector_and_integer) - << VectorTy << Ty << R; - } else - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_vector_and_scalar) - << VectorTy << Ty << R; + S.Diag(Call->getExprLoc(), + diag::err_bounds_safety_unsynchronized_indirect_param) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << ArgDepInfo.getDecl() + << !ArgDepInfo.isDeref() << IsDependent << DiagTy; + return false; + } + } + } - Kind = CK_BitCast; - return false; -} + for (const auto &ParmDepInfo : ParmInfo.getDepArgInfos()) { + if (std::none_of(ArgInfo.depinfo_begin(), ArgInfo.depinfo_end(), + [&ParmDepInfo](const DepArgInfo &ArgDepInfo) { + return ParmDepInfo.matches(ArgDepInfo); + })) { + S.Diag(ArgLoc, + diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } + } + } + } // End for loop -ExprResult Sema::prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr) { - QualType DestElemTy = VectorTy->castAs()->getElementType(); + return true; +} - if (DestElemTy == SplattedExpr->getType()) - return SplattedExpr; +static bool checkDynamicRangePointerAsParameter(Sema &S, FunctionDecl *FDecl, + CallExpr *Call) { + llvm::SmallPtrSet OutPointerDecls; - assert(DestElemTy->isFloatingType() || - DestElemTy->isIntegralOrEnumerationType()); + llvm::SmallVector CallArgInfos; + DynamicBoundArgumentInfo::DeclIndexMapTy DeclIndexMap; - CastKind CK; - if (VectorTy->isExtVectorType() && SplattedExpr->getType()->isBooleanType()) { - // OpenCL requires that we convert `true` boolean expressions to -1, but - // only when splatting vectors. - if (DestElemTy->isFloatingType()) { - // To avoid having to have a CK_BooleanToSignedFloating cast kind, we cast - // in two steps: boolean to signed integral, then to floating. - ExprResult CastExprRes = ImpCastExprToType(SplattedExpr, Context.IntTy, - CK_BooleanToSignedIntegral); - SplattedExpr = CastExprRes.get(); - CK = CK_IntegralToFloating; - } else { - CK = CK_BooleanToSignedIntegral; - } - } else { - ExprResult CastExprRes = SplattedExpr; - CK = PrepareScalarCast(CastExprRes, DestElemTy); - if (CastExprRes.isInvalid()) - return ExprError(); - SplattedExpr = CastExprRes.get(); + for (unsigned i = 0; i < Call->getNumArgs(); ++i) { + CallArgInfos.emplace_back(S.Context, Call->getArg(i)); + DeclIndexMap[CallArgInfos.back().getDecl()].push_back(i); } - return ImpCastExprToType(SplattedExpr, DestElemTy, CK); -} - -ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, - Expr *CastExpr, CastKind &Kind) { - assert(DestTy->isExtVectorType() && "Not an extended vector type!"); - QualType SrcTy = CastExpr->getType(); + for (auto &ArgInfo : CallArgInfos) { + ArgInfo.processDepArgInfos(DeclIndexMap); + } - // If SrcTy is a VectorType, the total size must match to explicitly cast to - // an ExtVectorType. - // In OpenCL, casts between vectors of different types are not allowed. - // (See OpenCL 6.2). - if (SrcTy->isVectorType()) { - if (!areLaxCompatibleVectorTypes(SrcTy, DestTy) || - (getLangOpts().OpenCL && - !Context.hasSameUnqualifiedType(DestTy, SrcTy))) { - Diag(R.getBegin(),diag::err_invalid_conversion_between_ext_vectors) - << DestTy << SrcTy << R; - return ExprError(); - } - Kind = CK_BitCast; - return CastExpr; + llvm::SmallVector FuncParmInfos; + for (unsigned i = 0; i < FDecl->getNumParams(); ++i) { + FuncParmInfos.emplace_back(FDecl->getParamDecl(i)); } - // All non-pointer scalars can be cast to ExtVector type. The appropriate - // conversion will take place first from scalar to elt type, and then - // splat from elt type to vector. - if (SrcTy->isPointerType()) - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_vector_and_scalar) - << DestTy << SrcTy << R; + unsigned MinNumArgs = std::min(Call->getNumArgs(), FDecl->getNumParams()); + // TODO: Report an error when a pointer to __ended_by is passed as va_arg + // (rdar://97041755). + for (unsigned i = 0; i < MinNumArgs; ++i) { + const auto &ArgInfo = CallArgInfos[i]; + const auto &ParmInfo = FuncParmInfos[i]; + Expr *ArgExp = Call->getArg(i)->IgnoreImpCasts(); + SourceLocation ArgLoc = ArgExp->getExprLoc(); - Kind = CK_VectorSplat; - return prepareVectorSplat(DestTy, CastExpr); -} + if (ParmInfo.isOutRangePointer() && !ArgInfo.isOutRangePointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getDecl()->getType() + << ParmInfo.getType() << Call->getSourceRange(); + return false; + } -ExprResult -Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, - Declarator &D, ParsedType &Ty, - SourceLocation RParenLoc, Expr *CastExpr) { - assert(!D.isInvalidType() && (CastExpr != nullptr) && - "ActOnCastExpr(): missing type or expr"); + if (!ParmInfo.isOutRangePointer() && ArgInfo.isOutRangePointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } - TypeSourceInfo *castTInfo = GetTypeForDeclaratorCast(D, CastExpr->getType()); - if (D.isInvalidType()) - return ExprError(); + if (ArgInfo.isAddrOfInBufParam()) { + bool HasInOutRange = false; + for (const auto &DI : ArgInfo.getDepArgInfos()) { + if (DI.Unlisted) + continue; + if (CallArgInfos[DI.Index].isOutRangePointer()) { + HasInOutRange = true; + break; + } + } - if (getLangOpts().CPlusPlus) { - // Check that there are no default arguments (C++ only). - CheckExtraCXXDefaultArguments(D); - } else { - // Make sure any TypoExprs have been dealt with. - ExprResult Res = CorrectDelayedTyposInExpr(CastExpr); - if (!Res.isUsable()) - return ExprError(); - CastExpr = Res.get(); - } + if (HasInOutRange) { + S.Diag(ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer) + << ArgInfo.getDecl()->getName() << /*ended_by*/ 4; + return false; + } + } - checkUnusedDeclAttributes(D); + // TODO: This diagnostic check should always be performed + // (rdar://138982703). The check is currently guarded to avoid potentially + // breaking the build. + if (ArgInfo.isEndInRet() && S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + // Keep it simple for now and disallow passing by out parameter. + QualType Ty = ArgExp->getType(); + bool IsIndirect = + Ty->isPointerType() && Ty->getPointeeType()->isPointerType(); + if (IsIndirect) { + const auto *ArgPVD = cast(ArgInfo.getDecl()); + const auto *Caller = cast(ArgPVD->getDeclContext()); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dependent_param_in_return) + << ArgPVD->getName() << /* __ended_by() */ 4 << Caller->getName() + << Caller->getReturnType(); + return false; + } + } - QualType castType = castTInfo->getType(); - Ty = CreateParsedType(castType, castTInfo); + // check parameter relationship + if (ParmInfo.isOutRangePointer()) { + assert(ArgInfo.isOutRangePointer()); + if (ParmInfo.getDepArgInfos().size() != ArgInfo.getDepArgInfos().size()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() << ParmInfo.getType() + << Call->getSourceRange(); + return false; + } - bool isVectorLiteral = false; + for (unsigned j = 0; j < ParmInfo.getDepArgInfos().size(); ++j) { + const auto &ArgDepInfo = ArgInfo.getDepArgInfos()[j]; + const auto &ParmDepInfo = ParmInfo.getDepArgInfos()[j]; + if (ArgDepInfo.Unlisted) { + const auto *DRPTy = + ArgInfo.getType()->getAs(); + assert(DRPTy); + unsigned IsDependent = 0; + + QualType DiagTy = ArgInfo.getType(); + if (!DRPTy->getEndPointer()) { + IsDependent = 1; + DiagTy = ArgDepInfo.getDecl()->getType(); + } - // Check for an altivec or OpenCL literal, - // i.e. all the elements are integer constants. - ParenExpr *PE = dyn_cast(CastExpr); - ParenListExpr *PLE = dyn_cast(CastExpr); - if ((getLangOpts().AltiVec || getLangOpts().ZVector || getLangOpts().OpenCL) - && castType->isVectorType() && (PE || PLE)) { - if (PLE && PLE->getNumExprs() == 0) { - Diag(PLE->getExprLoc(), diag::err_altivec_empty_initializer); - return ExprError(); - } - if (PE || PLE->getNumExprs() == 1) { - Expr *E = (PE ? PE->getSubExpr() : PLE->getExpr(0)); - if (!E->isTypeDependent() && !E->getType()->isVectorType()) - isVectorLiteral = true; + S.Diag(Call->getExprLoc(), + diag::err_bounds_safety_unsynchronized_indirect_param) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << ArgDepInfo.getDecl() + << !ArgDepInfo.isDeref() << IsDependent << DiagTy; + return false; + } + if (!ParmDepInfo.matches(ArgDepInfo)) { + S.Diag(ArgLoc, + diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() << ParmInfo.getType() + << Call->getSourceRange(); + return false; + } + } } - else - isVectorLiteral = true; - } + } // End for loop - // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' - // then handle it as such. - if (isVectorLiteral) - return BuildVectorLiteral(LParenLoc, RParenLoc, CastExpr, castTInfo); - - // If the Expr being casted is a ParenListExpr, handle it specially. - // This is not an AltiVec-style cast, so turn the ParenListExpr into a - // sequence of BinOp comma operators. - if (isa(CastExpr)) { - ExprResult Result = MaybeConvertParenListExprToParenExpr(S, CastExpr); - if (Result.isInvalid()) return ExprError(); - CastExpr = Result.get(); - } - - if (getLangOpts().CPlusPlus && !castType->isVoidType()) - Diag(LParenLoc, diag::warn_old_style_cast) << CastExpr->getSourceRange(); - - ObjC().CheckTollFreeBridgeCast(castType, CastExpr); - - ObjC().CheckObjCBridgeRelatedCast(castType, CastExpr); - - DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr); - - return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr); + return true; } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, - SourceLocation RParenLoc, Expr *E, - TypeSourceInfo *TInfo) { - assert((isa(E) || isa(E)) && - "Expected paren or paren list expression"); - - Expr **exprs; - unsigned numExprs; - Expr *subExpr; - SourceLocation LiteralLParenLoc, LiteralRParenLoc; - if (ParenListExpr *PE = dyn_cast(E)) { - LiteralLParenLoc = PE->getLParenLoc(); - LiteralRParenLoc = PE->getRParenLoc(); - exprs = PE->getExprs(); - numExprs = PE->getNumExprs(); - } else { // isa by assertion at function entrance - LiteralLParenLoc = cast(E)->getLParen(); - LiteralRParenLoc = cast(E)->getRParen(); - subExpr = cast(E)->getSubExpr(); - exprs = &subExpr; - numExprs = 1; - } - - QualType Ty = TInfo->getType(); - assert(Ty->isVectorType() && "Expected vector type"); - - SmallVector initExprs; - const VectorType *VTy = Ty->castAs(); - unsigned numElems = VTy->getNumElements(); +ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, + SourceLocation LParenLoc, + ArrayRef Args, + SourceLocation RParenLoc, Expr *Config, + bool IsExecConfig, ADLCallKind UsesADL) { + FunctionDecl *FDecl = dyn_cast_or_null(NDecl); + unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); - // '(...)' form of vector initialization in AltiVec: the number of - // initializers must be one or must match the size of the vector. - // If a single value is specified in the initializer then it will be - // replicated to all the components of the vector - if (CheckAltivecInitFromScalar(E->getSourceRange(), Ty, - VTy->getElementType())) - return ExprError(); - if (ShouldSplatAltivecScalarInCast(VTy)) { - // The number of initializers must be one or must match the size of the - // vector. If a single value is specified in the initializer then it will - // be replicated to all the components of the vector - if (numExprs == 1) { - QualType ElemTy = VTy->getElementType(); - ExprResult Literal = DefaultLvalueConversion(exprs[0]); - if (Literal.isInvalid()) - return ExprError(); - Literal = ImpCastExprToType(Literal.get(), ElemTy, - PrepareScalarCast(Literal, ElemTy)); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); + // Functions with 'interrupt' attribute cannot be called directly. + if (FDecl) { + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); + return ExprError(); } - else if (numExprs < numElems) { - Diag(E->getExprLoc(), - diag::err_incorrect_number_of_vector_initializers); + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called); return ExprError(); } - else - initExprs.append(exprs, exprs + numExprs); } - else { - // For OpenCL, when the number of initializers is a single value, - // it will be replicated to all components of the vector. - if (getLangOpts().OpenCL && VTy->getVectorKind() == VectorKind::Generic && - numExprs == 1) { - QualType ElemTy = VTy->getElementType(); - ExprResult Literal = DefaultLvalueConversion(exprs[0]); - if (Literal.isInvalid()) - return ExprError(); - Literal = ImpCastExprToType(Literal.get(), ElemTy, - PrepareScalarCast(Literal, ElemTy)); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); - } - initExprs.append(exprs, exprs + numExprs); + // X86 interrupt handlers may only call routines with attribute + // no_caller_saved_registers since there is no efficient way to + // save and restore the non-GPR state. + if (auto *Caller = getCurFunctionDecl()) { + if (Caller->hasAttr() || + Caller->hasAttr()) { + const TargetInfo &TI = Context.getTargetInfo(); + bool HasNonGPRRegisters = + TI.hasFeature("sse") || TI.hasFeature("x87") || TI.hasFeature("mmx"); + if (HasNonGPRRegisters && + (!FDecl || !FDecl->hasAttr())) { + Diag(Fn->getExprLoc(), diag::warn_anyx86_excessive_regsave) + << (Caller->hasAttr() ? 0 : 1); + if (FDecl) + Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl; + } + } } - // FIXME: This means that pretty-printing the final AST will produce curly - // braces instead of the original commas. - InitListExpr *initE = new (Context) InitListExpr(Context, LiteralLParenLoc, - initExprs, LiteralRParenLoc); - initE->setType(Ty); - return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE); -} - -ExprResult -Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) { - ParenListExpr *E = dyn_cast(OrigExpr); - if (!E) - return OrigExpr; - - ExprResult Result(E->getExpr(0)); - - for (unsigned i = 1, e = E->getNumExprs(); i != e && !Result.isInvalid(); ++i) - Result = ActOnBinOp(S, E->getExprLoc(), tok::comma, Result.get(), - E->getExpr(i)); - - if (Result.isInvalid()) return ExprError(); - - return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get()); -} - -ExprResult Sema::ActOnParenListExpr(SourceLocation L, - SourceLocation R, - MultiExprArg Val) { - return ParenListExpr::Create(Context, L, Val, R); -} - -bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr, - SourceLocation QuestionLoc) { - const Expr *NullExpr = LHSExpr; - const Expr *NonPointerExpr = RHSExpr; - Expr::NullPointerConstantKind NullKind = - NullExpr->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull); - if (NullKind == Expr::NPCK_NotNull) { - NullExpr = RHSExpr; - NonPointerExpr = LHSExpr; - NullKind = - NullExpr->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull); + // Promote the function operand. + // We special-case function promotion here because we only allow promoting + // builtin functions to function pointers in the callee of a call. + ExprResult Result; + QualType ResultTy; + if (BuiltinID && + Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) { + // Extract the return type from the (builtin) function pointer type. + // FIXME Several builtins still have setType in + // Sema::CheckBuiltinFunctionCall. One should review their definitions in + // Builtins.td to ensure they are correct before removing setType calls. + QualType FnPtrTy = Context.getPointerType(FDecl->getType()); + Result = ImpCastExprToType(Fn, FnPtrTy, CK_BuiltinFnToFnPtr).get(); + ResultTy = FDecl->getCallResultType(); + } else { + Result = CallExprUnaryConversions(Fn); + ResultTy = Context.BoolTy; } + if (Result.isInvalid()) + return ExprError(); + Fn = Result.get(); - if (NullKind == Expr::NPCK_NotNull) - return false; - - if (NullKind == Expr::NPCK_ZeroExpression) - return false; + // Check for a valid function type, but only if it is not a builtin which + // requires custom type checking. These will be handled by + // CheckBuiltinFunctionCall below just after creation of the call expression. + const FunctionType *FuncT = nullptr; + if (!BuiltinID || !Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { + retry: + if (const PointerType *PT = Fn->getType()->getAs()) { + // C99 6.5.2.2p1 - "The expression that denotes the called function shall + // have type pointer to function". + FuncT = PT->getPointeeType()->getAs(); + if (!FuncT) + return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) + << Fn->getType() << Fn->getSourceRange()); + } else if (const BlockPointerType *BPT = + Fn->getType()->getAs()) { + FuncT = BPT->getPointeeType()->castAs(); + } else { + // Handle calls to expressions of unknown-any type. + if (Fn->getType() == Context.UnknownAnyTy) { + ExprResult rewrite = rebuildUnknownAnyFunction(*this, Fn); + if (rewrite.isInvalid()) + return ExprError(); + Fn = rewrite.get(); + goto retry; + } - if (NullKind == Expr::NPCK_ZeroLiteral) { - // In this case, check to make sure that we got here from a "NULL" - // string in the source code. - NullExpr = NullExpr->IgnoreParenImpCasts(); - SourceLocation loc = NullExpr->getExprLoc(); - if (!findMacroSpelling(loc, "NULL")) - return false; + return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) + << Fn->getType() << Fn->getSourceRange()); + } } - int DiagType = (NullKind == Expr::NPCK_CXX11_nullptr); - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null) - << NonPointerExpr->getType() << DiagType - << NonPointerExpr->getSourceRange(); - return true; -} + // Get the number of parameters in the function prototype, if any. + // We will allocate space for max(Args.size(), NumParams) arguments + // in the call expression. + const auto *Proto = dyn_cast_or_null(FuncT); + unsigned NumParams = Proto ? Proto->getNumParams() : 0; -/// Return false if the condition expression is valid, true otherwise. -static bool checkCondition(Sema &S, const Expr *Cond, - SourceLocation QuestionLoc) { - QualType CondTy = Cond->getType(); + CallExpr *TheCall; + if (Config) { + assert(UsesADL == ADLCallKind::NotADL && + "CUDAKernelCallExpr should not use ADL"); + TheCall = CUDAKernelCallExpr::Create(Context, Fn, cast(Config), + Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams); + } else { + TheCall = + CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams, UsesADL); + } - // OpenCL v1.1 s6.3.i says the condition cannot be a floating point type. - if (S.getLangOpts().OpenCL && CondTy->isFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) - << CondTy << Cond->getSourceRange(); - return true; + if (!Context.isDependenceAllowed()) { + // Forget about the nulled arguments since typo correction + // do not handle them well. + TheCall->shrinkNumArgs(Args.size()); + // C cannot always handle TypoExpr nodes in builtin calls and direct + // function calls as their argument checking don't necessarily handle + // dependent types properly, so make sure any TypoExprs have been + // dealt with. + ExprResult Result = CorrectDelayedTyposInExpr(TheCall); + if (!Result.isUsable()) return ExprError(); + CallExpr *TheOldCall = TheCall; + TheCall = dyn_cast(Result.get()); + bool CorrectedTypos = TheCall != TheOldCall; + if (!TheCall) return Result; + Args = llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()); + + // A new call expression node was created if some typos were corrected. + // However it may not have been constructed with enough storage. In this + // case, rebuild the node with enough storage. The waste of space is + // immaterial since this only happens when some typos were corrected. + if (CorrectedTypos && Args.size() < NumParams) { + if (Config) + TheCall = CUDAKernelCallExpr::Create( + Context, Fn, cast(Config), Args, ResultTy, VK_PRValue, + RParenLoc, CurFPFeatureOverrides(), NumParams); + else + TheCall = + CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams, UsesADL); + } + // We can now handle the nulled arguments for the default arguments. + TheCall->setNumArgsUnsafe(std::max(Args.size(), NumParams)); } - // C99 6.5.15p2 - if (CondTy->isScalarType()) return false; + // Bail out early if calling a builtin with custom type checking. + if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { + ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); + if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID)) + E = CheckForImmediateInvocation(E, FDecl); + return E; + } - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) - << CondTy << Cond->getSourceRange(); - return true; -} + if (getLangOpts().CUDA) { + if (Config) { + // CUDA: Kernel calls must be to global functions + if (FDecl && !FDecl->hasAttr()) + return ExprError(Diag(LParenLoc,diag::err_kern_call_not_global_function) + << FDecl << Fn->getSourceRange()); -/// Return false if the NullExpr can be promoted to PointerTy, -/// true otherwise. -static bool checkConditionalNullPointer(Sema &S, ExprResult &NullExpr, - QualType PointerTy) { - if ((!PointerTy->isAnyPointerType() && !PointerTy->isBlockPointerType()) || - !NullExpr.get()->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) - return true; + // CUDA: Kernel function must have 'void' return type + if (!FuncT->getReturnType()->isVoidType() && + !FuncT->getReturnType()->getAs() && + !FuncT->getReturnType()->isInstantiationDependentType()) + return ExprError(Diag(LParenLoc, diag::err_kern_type_not_void_return) + << Fn->getType() << Fn->getSourceRange()); + } else { + // CUDA: Calls to global functions must be configured + if (FDecl && FDecl->hasAttr()) + return ExprError(Diag(LParenLoc, diag::err_global_call_not_config) + << FDecl << Fn->getSourceRange()); + } + } - NullExpr = S.ImpCastExprToType(NullExpr.get(), PointerTy, CK_NullToPointer); - return false; -} + // Check for a valid return type + if (CheckCallReturnType(FuncT->getReturnType(), Fn->getBeginLoc(), TheCall, + FDecl)) + return ExprError(); -/// Checks compatibility between two pointers and return the resulting -/// type. -static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); + // We know the result type of the call, set it. + TheCall->setType(FuncT->getCallResultType(Context)); + TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); - if (S.Context.hasSameType(LHSTy, RHSTy)) { - // Two identical pointers types are always compatible. - return S.Context.getCommonSugaredType(LHSTy, RHSTy); + // WebAssembly tables can't be used as arguments. + if (Context.getTargetInfo().getTriple().isWasm()) { + for (const Expr *Arg : Args) { + if (Arg && Arg->getType()->isWebAssemblyTableType()) { + return ExprError(Diag(Arg->getExprLoc(), + diag::err_wasm_table_as_function_parameter)); + } + } } - QualType lhptee, rhptee; - - // Get the pointee types. - bool IsBlockPointer = false; - if (const BlockPointerType *LHSBTy = LHSTy->getAs()) { - lhptee = LHSBTy->getPointeeType(); - rhptee = RHSTy->castAs()->getPointeeType(); - IsBlockPointer = true; + if (Proto) { + if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, + IsExecConfig)) + return ExprError(); } else { - lhptee = LHSTy->castAs()->getPointeeType(); - rhptee = RHSTy->castAs()->getPointeeType(); - } + assert(isa(FuncT) && "Unknown FunctionType!"); - // C99 6.5.15p6: If both operands are pointers to compatible types or to - // differently qualified versions of compatible types, the result type is - // a pointer to an appropriately qualified version of the composite - // type. + if (FDecl) { + // Check if we have too few/too many template arguments, based + // on our knowledge of the function definition. + const FunctionDecl *Def = nullptr; + if (FDecl->hasBody(Def) && Args.size() != Def->param_size()) { + Proto = Def->getType()->getAs(); + if (!Proto || !(Proto->isVariadic() && Args.size() >= Def->param_size())) + Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) + << (Args.size() > Def->param_size()) << FDecl << Fn->getSourceRange(); + } - // Only CVR-qualifiers exist in the standard, and the differently-qualified - // clause doesn't make sense for our extensions. E.g. address space 2 should - // be incompatible with address space 3: they may live on different devices or - // anything. - Qualifiers lhQual = lhptee.getQualifiers(); - Qualifiers rhQual = rhptee.getQualifiers(); + // If the function we're calling isn't a function prototype, but we have + // a function prototype from a prior declaratiom, use that prototype. + if (!FDecl->hasPrototype()) + Proto = FDecl->getType()->getAs(); + } - LangAS ResultAddrSpace = LangAS::Default; - LangAS LAddrSpace = lhQual.getAddressSpace(); - LangAS RAddrSpace = rhQual.getAddressSpace(); + // If we still haven't found a prototype to use but there are arguments to + // the call, diagnose this as calling a function without a prototype. + // However, if we found a function declaration, check to see if + // -Wdeprecated-non-prototype was disabled where the function was declared. + // If so, we will silence the diagnostic here on the assumption that this + // interface is intentional and the user knows what they're doing. We will + // also silence the diagnostic if there is a function declaration but it + // was implicitly defined (the user already gets diagnostics about the + // creation of the implicit function declaration, so the additional warning + // is not helpful). + if (!Proto && !Args.empty() && + (!FDecl || (!FDecl->isImplicit() && + !Diags.isIgnored(diag::warn_strict_uses_without_prototype, + FDecl->getLocation())))) + Diag(LParenLoc, diag::warn_strict_uses_without_prototype) + << (FDecl != nullptr) << FDecl; - // OpenCL v1.1 s6.5 - Conversion between pointers to distinct address - // spaces is disallowed. - if (lhQual.isAddressSpaceSupersetOf(rhQual)) - ResultAddrSpace = LAddrSpace; - else if (rhQual.isAddressSpaceSupersetOf(lhQual)) - ResultAddrSpace = RAddrSpace; - else { - S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSTy << RHSTy << 2 << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + // Promote the arguments (C99 6.5.2.2p6). + for (unsigned i = 0, e = Args.size(); i != e; i++) { + Expr *Arg = Args[i]; - unsigned MergedCVRQual = lhQual.getCVRQualifiers() | rhQual.getCVRQualifiers(); - auto LHSCastKind = CK_BitCast, RHSCastKind = CK_BitCast; - lhQual.removeCVRQualifiers(); - rhQual.removeCVRQualifiers(); + if (Proto && i < Proto->getNumParams()) { + InitializedEntity Entity = InitializedEntity::InitializeParameter( + Context, Proto->getParamType(i), Proto->isParamConsumed(i)); + ExprResult ArgE = + PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (ArgE.isInvalid()) + return true; - if (lhQual.getPointerAuth() != rhQual.getPointerAuth()) { - S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) - << LHSTy << RHSTy - << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + Arg = ArgE.getAs(); - // OpenCL v2.0 specification doesn't extend compatibility of type qualifiers - // (C99 6.7.3) for address spaces. We assume that the check should behave in - // the same manner as it's defined for CVR qualifiers, so for OpenCL two - // qual types are compatible iff - // * corresponded types are compatible - // * CVR qualifiers are equal - // * address spaces are equal - // Thus for conditional operator we merge CVR and address space unqualified - // pointees and if there is a composite type we return a pointer to it with - // merged qualifiers. - LHSCastKind = - LAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; - RHSCastKind = - RAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; - lhQual.removeAddressSpace(); - rhQual.removeAddressSpace(); + } else { + ExprResult ArgE = DefaultArgumentPromotion(Arg); - lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual); - rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual); + if (ArgE.isInvalid()) + return true; - QualType CompositeTy = S.Context.mergeTypes( - lhptee, rhptee, /*OfBlockPointer=*/false, /*Unqualified=*/false, - /*BlockReturnType=*/false, /*IsConditionalOperator=*/true); + Arg = ArgE.getAs(); + } - if (CompositeTy.isNull()) { - // In this situation, we assume void* type. No especially good - // reason, but this is what gcc does, and we do have to pick - // to get a consistent AST. - QualType incompatTy; - incompatTy = S.Context.getPointerType( - S.Context.getAddrSpaceQualType(S.Context.VoidTy, ResultAddrSpace)); - LHS = S.ImpCastExprToType(LHS.get(), incompatTy, LHSCastKind); - RHS = S.ImpCastExprToType(RHS.get(), incompatTy, RHSCastKind); + if (RequireCompleteType(Arg->getBeginLoc(), Arg->getType(), + diag::err_call_incomplete_argument, Arg)) + return ExprError(); - // FIXME: For OpenCL the warning emission and cast to void* leaves a room - // for casts between types with incompatible address space qualifiers. - // For the following code the compiler produces casts between global and - // local address spaces of the corresponded innermost pointees: - // local int *global *a; - // global int *global *b; - // a = (0 ? a : b); // see C99 6.5.16.1.p1. - S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + TheCall->setArg(i, Arg); + } + TheCall->computeDependence(); + } - return incompatTy; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && FDecl) { + // FIXME: We need to support function pointers and blocks that don't have + // function decl. + if (!checkDynamicCountPointerAsParameter(*this, FDecl, TheCall)) + return ExprError(); + if (getLangOpts().BoundsSafety) + if (!checkDynamicRangePointerAsParameter(*this, FDecl, TheCall)) + return ExprError(); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // The pointer types are compatible. - // In case of OpenCL ResultTy should have the address space qualifier - // which is a superset of address spaces of both the 2nd and the 3rd - // operands of the conditional operator. - QualType ResultTy = [&, ResultAddrSpace]() { - if (S.getLangOpts().OpenCL) { - Qualifiers CompositeQuals = CompositeTy.getQualifiers(); - CompositeQuals.setAddressSpace(ResultAddrSpace); - return S.Context - .getQualifiedType(CompositeTy.getUnqualifiedType(), CompositeQuals) - .withCVRQualifiers(MergedCVRQual); - } - return CompositeTy.withCVRQualifiers(MergedCVRQual); - }(); - if (IsBlockPointer) - ResultTy = S.Context.getBlockPointerType(ResultTy); - else - ResultTy = S.Context.getPointerType(ResultTy); - - LHS = S.ImpCastExprToType(LHS.get(), ResultTy, LHSCastKind); - RHS = S.ImpCastExprToType(RHS.get(), ResultTy, RHSCastKind); - return ResultTy; -} + if (CXXMethodDecl *Method = dyn_cast_or_null(FDecl)) + if (Method->isImplicitObjectMemberFunction()) + return ExprError(Diag(LParenLoc, diag::err_member_call_without_object) + << Fn->getSourceRange() << 0); -/// Return the resulting type when the operands are both block pointers. -static QualType checkConditionalBlockPointerCompatibility(Sema &S, - ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); + // Check for sentinels + if (NDecl) + DiagnoseSentinelCalls(NDecl, LParenLoc, Args); - if (!LHSTy->isBlockPointerType() || !RHSTy->isBlockPointerType()) { - if (LHSTy->isVoidPointerType() || RHSTy->isVoidPointerType()) { - QualType destType = S.Context.getPointerType(S.Context.VoidTy); - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); - return destType; + // Warn for unions passing across security boundary (CMSE). + if (FuncT != nullptr && FuncT->getCmseNSCallAttr()) { + for (unsigned i = 0, e = Args.size(); i != e; i++) { + if (const auto *RT = + dyn_cast(Args[i]->getType().getCanonicalType())) { + if (RT->getDecl()->isOrContainsUnion()) + Diag(Args[i]->getBeginLoc(), diag::warn_cmse_nonsecure_union) + << 0 << i; + } } - S.Diag(Loc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); } - // We have 2 block pointer types. - return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); -} - -/// Return the resulting type when the operands are both pointers. -static QualType -checkConditionalObjectPointersCompatibility(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - // get the pointer types - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); + // Do special checking on direct calls to functions. + if (FDecl) { + if (CheckFunctionCall(FDecl, TheCall, Proto)) + return ExprError(); - // get the "pointed to" types - QualType lhptee = LHSTy->castAs()->getPointeeType(); - QualType rhptee = RHSTy->castAs()->getPointeeType(); + checkFortifiedBuiltinMemoryFunction(FDecl, TheCall); - // ignore qualifiers on void (C99 6.5.15p3, clause 6) - if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { - // Figure out necessary qualifiers (C99 6.5.15p6) - QualType destPointee - = S.Context.getQualifiedType(lhptee, rhptee.getQualifiers()); - QualType destType = S.Context.getPointerType(destPointee); - // Add qualifiers if necessary. - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_NoOp); - // Promote to void*. - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); - return destType; - } - if (rhptee->isVoidType() && lhptee->isIncompleteOrObjectType()) { - QualType destPointee - = S.Context.getQualifiedType(rhptee, lhptee.getQualifiers()); - QualType destType = S.Context.getPointerType(destPointee); - // Add qualifiers if necessary. - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_NoOp); - // Promote to void*. - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); - return destType; + if (BuiltinID) + return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); + } else if (NDecl) { + if (CheckPointerCall(NDecl, TheCall, Proto)) + return ExprError(); + } else { + if (CheckOtherCall(TheCall, Proto)) + return ExprError(); } - return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!BoundsSafetyCheckResolvedCall(FDecl, TheCall, Proto)) + return ExprError(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } -/// Return false if the first expression is not an integer and the second -/// expression is not a pointer, true otherwise. -static bool checkPointerIntegerMismatch(Sema &S, ExprResult &Int, - Expr* PointerExpr, SourceLocation Loc, - bool IsIntFirstExpr) { - if (!PointerExpr->getType()->isPointerType() || - !Int.get()->getType()->isIntegerType()) - return false; +ExprResult +Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, + SourceLocation RParenLoc, Expr *InitExpr) { + assert(Ty && "ActOnCompoundLiteral(): missing type"); + assert(InitExpr && "ActOnCompoundLiteral(): missing expression"); - Expr *Expr1 = IsIntFirstExpr ? Int.get() : PointerExpr; - Expr *Expr2 = IsIntFirstExpr ? PointerExpr : Int.get(); + TypeSourceInfo *TInfo; + QualType literalType = GetTypeFromParser(Ty, &TInfo); + if (!TInfo) + TInfo = Context.getTrivialTypeSourceInfo(literalType); - S.Diag(Loc, diag::ext_typecheck_cond_pointer_integer_mismatch) - << Expr1->getType() << Expr2->getType() - << Expr1->getSourceRange() << Expr2->getSourceRange(); - Int = S.ImpCastExprToType(Int.get(), PointerExpr->getType(), - CK_IntegralToPointer); - return true; + return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr); } -/// Simple conversion between integer and floating point types. -/// -/// Used when handling the OpenCL conditional operator where the -/// condition is a vector while the other operands are scalar. -/// -/// OpenCL v1.1 s6.3.i and s6.11.6 together require that the scalar -/// types are either integer or floating type. Between the two -/// operands, the type with the higher rank is defined as the "result -/// type". The other operand needs to be promoted to the same type. No -/// other type promotion is allowed. We cannot use -/// UsualArithmeticConversions() for this purpose, since it always -/// promotes promotable types. -static QualType OpenCLArithmeticConversions(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation QuestionLoc) { - LHS = S.DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = - S.Context.getCanonicalType(LHS.get()->getType()).getUnqualifiedType(); - QualType RHSType = - S.Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); - - if (!LHSType->isIntegerType() && !LHSType->isRealFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) - << LHSType << LHS.get()->getSourceRange(); - return QualType(); - } +ExprResult +Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *LiteralExpr) { + QualType literalType = TInfo->getType(); - if (!RHSType->isIntegerType() && !RHSType->isRealFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) - << RHSType << RHS.get()->getSourceRange(); - return QualType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // XXX: as a hack, with -fbounds-safety enabled, we try to peek into the type of + // the expression being used in the initializer list. Clang's initialization + // system doesn't have hooks that allow inference to work, the type that we're + // going to pass here will be piped through deeply; type deduction is generally + // not possible. + if (getLangOpts().BoundsSafety && literalType->isPointerType()) { + bool InferredTypeFromInitList = false; + if (auto *InitList = dyn_cast(LiteralExpr)) { + if (InitList->getNumInits() > 0) { + QualType InitTy = InitList->getInit(0)->getType(); + if (InitTy->isPointerType() && !InitTy->isBoundsAttributedType()) { + literalType = deduceCastPointerAttributes( + literalType, InitList->getInit(0)->getType()); + InferredTypeFromInitList = true; + } + } + } + // If we don't find anything to start from, assume __bidi_indexable. + if (!InferredTypeFromInitList) { + literalType = Context.getBoundsSafetyPointerType( + literalType, BoundsSafetyPointerAttributes::bidiIndexable()); + } + TInfo->overrideType(literalType); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // If both types are identical, no conversion is needed. - if (LHSType == RHSType) - return LHSType; - - // Now handle "real" floating types (i.e. float, double, long double). - if (LHSType->isRealFloatingType() || RHSType->isRealFloatingType()) - return handleFloatConversion(S, LHS, RHS, LHSType, RHSType, - /*IsCompAssign = */ false); - - // Finally, we have two differing integer types. - return handleIntegerConversion - (S, LHS, RHS, LHSType, RHSType, /*IsCompAssign = */ false); -} + if (literalType->isArrayType()) { + if (RequireCompleteSizedType( + LParenLoc, Context.getBaseElementType(literalType), + diag::err_array_incomplete_or_sizeless_type, + SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) + return ExprError(); + if (literalType->isVariableArrayType()) { + // C23 6.7.10p4: An entity of variable length array type shall not be + // initialized except by an empty initializer. + // + // The C extension warnings are issued from ParseBraceInitializer() and + // do not need to be issued here. However, we continue to issue an error + // in the case there are initializers or we are compiling C++. We allow + // use of VLAs in C++, but it's not clear we want to allow {} to zero + // init a VLA in C++ in all cases (such as with non-trivial constructors). + // FIXME: should we allow this construct in C++ when it makes sense to do + // so? + // + // But: C99-C23 6.5.2.5 Compound literals constraint 1: The type name + // shall specify an object type or an array of unknown size, but not a + // variable length array type. This seems odd, as it allows 'int a[size] = + // {}', but forbids 'int *a = (int[size]){}'. As this is what the standard + // says, this is what's implemented here for C (except for the extension + // that permits constant foldable size arrays) -/// Convert scalar operands to a vector that matches the -/// condition in length. -/// -/// Used when handling the OpenCL conditional operator where the -/// condition is a vector while the other operands are scalar. -/// -/// We first compute the "result type" for the scalar operands -/// according to OpenCL v1.1 s6.3.i. Both operands are then converted -/// into a vector of that type where the length matches the condition -/// vector type. s6.11.6 requires that the element types of the result -/// and the condition must have the same number of bits. -static QualType -OpenCLConvertScalarsToVectors(Sema &S, ExprResult &LHS, ExprResult &RHS, - QualType CondTy, SourceLocation QuestionLoc) { - QualType ResTy = OpenCLArithmeticConversions(S, LHS, RHS, QuestionLoc); - if (ResTy.isNull()) return QualType(); + auto diagID = LangOpts.CPlusPlus + ? diag::err_variable_object_no_init + : diag::err_compound_literal_with_vla_type; + if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, + diagID)) + return ExprError(); + } + } else if (!literalType->isDependentType() && + RequireCompleteType(LParenLoc, literalType, + diag::err_typecheck_decl_incomplete_type, + SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) + return ExprError(); - const VectorType *CV = CondTy->getAs(); - assert(CV); + InitializedEntity Entity + = InitializedEntity::InitializeCompoundLiteralInit(TInfo); + InitializationKind Kind + = InitializationKind::CreateCStyleCast(LParenLoc, + SourceRange(LParenLoc, RParenLoc), + /*InitList=*/true); + InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr, + &literalType); + if (Result.isInvalid()) + return ExprError(); + LiteralExpr = Result.get(); - // Determine the vector result type - unsigned NumElements = CV->getNumElements(); - QualType VectorTy = S.Context.getExtVectorType(ResTy, NumElements); + bool isFileScope = !CurContext->isFunctionOrMethod(); - // Ensure that all types have the same number of bits - if (S.Context.getTypeSize(CV->getElementType()) - != S.Context.getTypeSize(ResTy)) { - // Since VectorTy is created internally, it does not pretty print - // with an OpenCL name. Instead, we just print a description. - std::string EleTyName = ResTy.getUnqualifiedType().getAsString(); - SmallString<64> Str; - llvm::raw_svector_ostream OS(Str); - OS << "(vector of " << NumElements << " '" << EleTyName << "' values)"; - S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) - << CondTy << OS.str(); - return QualType(); - } - - // Convert operands to the vector result type - LHS = S.ImpCastExprToType(LHS.get(), VectorTy, CK_VectorSplat); - RHS = S.ImpCastExprToType(RHS.get(), VectorTy, CK_VectorSplat); + // In C, compound literals are l-values for some reason. + // For GCC compatibility, in C++, file-scope array compound literals with + // constant initializers are also l-values, and compound literals are + // otherwise prvalues. + // + // (GCC also treats C++ list-initialized file-scope array prvalues with + // constant initializers as l-values, but that's non-conforming, so we don't + // follow it there.) + // + // FIXME: It would be better to handle the lvalue cases as materializing and + // lifetime-extending a temporary object, but our materialized temporaries + // representation only supports lifetime extension from a variable, not "out + // of thin air". + // FIXME: For C++, we might want to instead lifetime-extend only if a pointer + // is bound to the result of applying array-to-pointer decay to the compound + // literal. + // FIXME: GCC supports compound literals of reference type, which should + // obviously have a value kind derived from the kind of reference involved. + ExprValueKind VK = + (getLangOpts().CPlusPlus && !(isFileScope && literalType->isArrayType())) + ? VK_PRValue + : VK_LValue; - return VectorTy; -} + if (isFileScope) + if (auto ILE = dyn_cast(LiteralExpr)) + for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) { + Expr *Init = ILE->getInit(i); + ILE->setInit(i, ConstantExpr::Create(Context, Init)); + } -/// Return false if this is a valid OpenCL condition vector -static bool checkOpenCLConditionVector(Sema &S, Expr *Cond, - SourceLocation QuestionLoc) { - // OpenCL v1.1 s6.11.6 says the elements of the vector must be of - // integral type. - const VectorType *CondTy = Cond->getType()->getAs(); - assert(CondTy); - QualType EleTy = CondTy->getElementType(); - if (EleTy->isIntegerType()) return false; + auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, + VK, LiteralExpr, isFileScope); + if (isFileScope) { + if (!LiteralExpr->isTypeDependent() && + !LiteralExpr->isValueDependent() && + !literalType->isDependentType()) // C99 6.5.2.5p3 + if (CheckForConstantInitializer(LiteralExpr)) + return ExprError(); + } else if (literalType.getAddressSpace() != LangAS::opencl_private && + literalType.getAddressSpace() != LangAS::Default) { + // Embedded-C extensions to C99 6.5.2.5: + // "If the compound literal occurs inside the body of a function, the + // type name shall not be qualified by an address-space qualifier." + Diag(LParenLoc, diag::err_compound_literal_with_address_space) + << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()); + return ExprError(); + } - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) - << Cond->getType() << Cond->getSourceRange(); - return true; -} + if (!isFileScope && !getLangOpts().CPlusPlus) { + // Compound literals that have automatic storage duration are destroyed at + // the end of the scope in C; in C++, they're just temporaries. -/// Return false if the vector condition type and the vector -/// result type are compatible. -/// -/// OpenCL v1.1 s6.11.6 requires that both vector types have the same -/// number of elements, and their element types have the same number -/// of bits. -static bool checkVectorResult(Sema &S, QualType CondTy, QualType VecResTy, - SourceLocation QuestionLoc) { - const VectorType *CV = CondTy->getAs(); - const VectorType *RV = VecResTy->getAs(); - assert(CV && RV); + // Emit diagnostics if it is or contains a C union type that is non-trivial + // to destruct. + if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + NTCUC_CompoundLiteral, NTCUK_Destruct); - if (CV->getNumElements() != RV->getNumElements()) { - S.Diag(QuestionLoc, diag::err_conditional_vector_size) - << CondTy << VecResTy; - return true; + // Diagnose jumps that enter or exit the lifetime of the compound literal. + if (literalType.isDestructedType()) { + Cleanup.setExprNeedsCleanups(true); + ExprCleanupObjects.push_back(E); + getCurFunction()->setHasBranchProtectedScope(); + } } - QualType CVE = CV->getElementType(); - QualType RVE = RV->getElementType(); - - if (S.Context.getTypeSize(CVE) != S.Context.getTypeSize(RVE)) { - S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) - << CondTy << VecResTy; - return true; - } + if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnionInInitializer(E->getInitializer(), + E->getInitializer()->getExprLoc()); - return false; + return MaybeBindToTemporary(E); } -/// Return the resulting type for the conditional operator in -/// OpenCL (aka "ternary selection operator", OpenCL v1.1 -/// s6.3.i) when the condition is a vector type. -static QualType -OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond, - ExprResult &LHS, ExprResult &RHS, - SourceLocation QuestionLoc) { - Cond = S.DefaultFunctionArrayLvalueConversion(Cond.get()); - if (Cond.isInvalid()) - return QualType(); - QualType CondTy = Cond.get()->getType(); +ExprResult +Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, + SourceLocation RBraceLoc) { + // Only produce each kind of designated initialization diagnostic once. + SourceLocation FirstDesignator; + bool DiagnosedArrayDesignator = false; + bool DiagnosedNestedDesignator = false; + bool DiagnosedMixedDesignator = false; - if (checkOpenCLConditionVector(S, Cond.get(), QuestionLoc)) - return QualType(); + // Check that any designated initializers are syntactically valid in the + // current language mode. + for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { + if (auto *DIE = dyn_cast(InitArgList[I])) { + if (FirstDesignator.isInvalid()) + FirstDesignator = DIE->getBeginLoc(); - // If either operand is a vector then find the vector type of the - // result as specified in OpenCL v1.1 s6.3.i. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - bool IsBoolVecLang = - !S.getLangOpts().OpenCL && !S.getLangOpts().OpenCLCPlusPlus; - QualType VecResTy = - S.CheckVectorOperands(LHS, RHS, QuestionLoc, - /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ IsBoolVecLang, - /*ReportInvalid*/ true); - if (VecResTy.isNull()) - return QualType(); - // The result type must match the condition type as specified in - // OpenCL v1.1 s6.11.6. - if (checkVectorResult(S, CondTy, VecResTy, QuestionLoc)) - return QualType(); - return VecResTy; - } + if (!getLangOpts().CPlusPlus) + break; - // Both operands are scalar. - return OpenCLConvertScalarsToVectors(S, LHS, RHS, CondTy, QuestionLoc); -} + if (!DiagnosedNestedDesignator && DIE->size() > 1) { + DiagnosedNestedDesignator = true; + Diag(DIE->getBeginLoc(), diag::ext_designated_init_nested) + << DIE->getDesignatorsSourceRange(); + } -/// Return true if the Expr is block type -static bool checkBlockType(Sema &S, const Expr *E) { - if (const CallExpr *CE = dyn_cast(E)) { - QualType Ty = CE->getCallee()->getType(); - if (Ty->isBlockPointerType()) { - S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); - return true; + for (auto &Desig : DIE->designators()) { + if (!Desig.isFieldDesignator() && !DiagnosedArrayDesignator) { + DiagnosedArrayDesignator = true; + Diag(Desig.getBeginLoc(), diag::ext_designated_init_array) + << Desig.getSourceRange(); + } + } + + if (!DiagnosedMixedDesignator && + !isa(InitArgList[0])) { + DiagnosedMixedDesignator = true; + Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) + << DIE->getSourceRange(); + Diag(InitArgList[0]->getBeginLoc(), diag::note_designated_init_mixed) + << InitArgList[0]->getSourceRange(); + } + } else if (getLangOpts().CPlusPlus && !DiagnosedMixedDesignator && + isa(InitArgList[0])) { + DiagnosedMixedDesignator = true; + auto *DIE = cast(InitArgList[0]); + Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) + << DIE->getSourceRange(); + Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed) + << InitArgList[I]->getSourceRange(); } } - return false; -} -/// Note that LHS is not null here, even if this is the gnu "x ?: y" extension. -/// In that case, LHS = cond. -/// C99 6.5.15 -QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, - ExprResult &RHS, ExprValueKind &VK, - ExprObjectKind &OK, - SourceLocation QuestionLoc) { + if (FirstDesignator.isValid()) { + // Only diagnose designated initiaization as a C++20 extension if we didn't + // already diagnose use of (non-C++20) C99 designator syntax. + if (getLangOpts().CPlusPlus && !DiagnosedArrayDesignator && + !DiagnosedNestedDesignator && !DiagnosedMixedDesignator) { + Diag(FirstDesignator, getLangOpts().CPlusPlus20 + ? diag::warn_cxx17_compat_designated_init + : diag::ext_cxx_designated_init); + } else if (!getLangOpts().CPlusPlus && !getLangOpts().C99) { + Diag(FirstDesignator, diag::ext_designated_init); + } + } - ExprResult LHSResult = CheckPlaceholderExpr(LHS.get()); - if (!LHSResult.isUsable()) return QualType(); - LHS = LHSResult; + return BuildInitList(LBraceLoc, InitArgList, RBraceLoc); +} - ExprResult RHSResult = CheckPlaceholderExpr(RHS.get()); - if (!RHSResult.isUsable()) return QualType(); - RHS = RHSResult; +ExprResult +Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, + SourceLocation RBraceLoc) { + // Semantic analysis for initializers is done by ActOnDeclarator() and + // CheckInitializer() - it requires knowledge of the object being initialized. - // C++ is sufficiently different to merit its own checker. - if (getLangOpts().CPlusPlus) - return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc); + // Immediately handle non-overload placeholders. Overloads can be + // resolved contextually, but everything else here can't. + for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { + if (InitArgList[I]->getType()->isNonOverloadPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(InitArgList[I]); - VK = VK_PRValue; - OK = OK_Ordinary; + // Ignore failures; dropping the entire initializer list because + // of one failure would be terrible for indexing/etc. + if (result.isInvalid()) continue; - if (Context.isDependenceAllowed() && - (Cond.get()->isTypeDependent() || LHS.get()->isTypeDependent() || - RHS.get()->isTypeDependent())) { - assert(!getLangOpts().CPlusPlus); - assert((Cond.get()->containsErrors() || LHS.get()->containsErrors() || - RHS.get()->containsErrors()) && - "should only occur in error-recovery path."); - return Context.DependentTy; + InitArgList[I] = result.get(); + } } - // The OpenCL operator with a vector condition is sufficiently - // different to merit its own checker. - if ((getLangOpts().OpenCL && Cond.get()->getType()->isVectorType()) || - Cond.get()->getType()->isExtVectorType()) - return OpenCLCheckVectorConditional(*this, Cond, LHS, RHS, QuestionLoc); + InitListExpr *E = + new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); + E->setType(Context.VoidTy); // FIXME: just a place holder for now. + return E; +} - // First, check the condition. - Cond = UsualUnaryConversions(Cond.get()); - if (Cond.isInvalid()) - return QualType(); - if (checkCondition(*this, Cond.get(), QuestionLoc)) - return QualType(); +void Sema::maybeExtendBlockObject(ExprResult &E) { + assert(E.get()->getType()->isBlockPointerType()); + assert(E.get()->isPRValue()); - // Handle vectors. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); + // Only do this in an r-value context. + if (!getLangOpts().ObjCAutoRefCount) return; - QualType ResTy = - UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + E = ImplicitCastExpr::Create( + Context, E.get()->getType(), CK_ARCExtendBlockObject, E.get(), + /*base path*/ nullptr, VK_PRValue, FPOptionsOverride()); + Cleanup.setExprNeedsCleanups(true); +} - // WebAssembly tables are not allowed as conditional LHS or RHS. - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { - Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } +CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) { + // Both Src and Dest are scalar types, i.e. arithmetic or pointer. + // Also, callers should have filtered out the invalid cases with + // pointers. Everything else should be possible. - // Diagnose attempts to convert between __ibm128, __float128 and long double - // where such conversions currently can't be handled. - if (unsupportedTypeConversion(*this, LHSTy, RHSTy)) { - Diag(QuestionLoc, - diag::err_typecheck_cond_incompatible_operands) << LHSTy << RHSTy - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } + QualType SrcTy = Src.get()->getType(); + if (Context.hasSameUnqualifiedType(SrcTy, DestTy)) + return CK_NoOp; - // OpenCL v2.0 s6.12.5 - Blocks cannot be used as expressions of the ternary - // selection operator (?:). - if (getLangOpts().OpenCL && - ((int)checkBlockType(*this, LHS.get()) | (int)checkBlockType(*this, RHS.get()))) { - return QualType(); - } + switch (Type::ScalarTypeKind SrcKind = SrcTy->getScalarTypeKind()) { + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); - // If both operands have arithmetic type, do the usual arithmetic conversions - // to find a common type: C99 6.5.15p3,5. - if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { - // Disallow invalid arithmetic conversions, such as those between bit- - // precise integers types of different sizes, or between a bit-precise - // integer and another type. - if (ResTy.isNull() && (LHSTy->isBitIntType() || RHSTy->isBitIntType())) { - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); + case Type::STK_CPointer: + case Type::STK_BlockPointer: + case Type::STK_ObjCObjectPointer: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_CPointer: { + LangAS SrcAS = SrcTy->getPointeeType().getAddressSpace(); + LangAS DestAS = DestTy->getPointeeType().getAddressSpace(); + if (SrcAS != DestAS) + return CK_AddressSpaceConversion; + if (Context.hasCvrSimilarType(SrcTy, DestTy)) + return CK_NoOp; + return CK_BitCast; } + case Type::STK_BlockPointer: + return (SrcKind == Type::STK_BlockPointer + ? CK_BitCast : CK_AnyPointerToBlockPointerCast); + case Type::STK_ObjCObjectPointer: + if (SrcKind == Type::STK_ObjCObjectPointer) + return CK_BitCast; + if (SrcKind == Type::STK_CPointer) + return CK_CPointerToObjCPointerCast; + maybeExtendBlockObject(Src); + return CK_BlockPointerToObjCPointerCast; + case Type::STK_Bool: + return CK_PointerToBoolean; + case Type::STK_Integral: + return CK_PointerToIntegral; + case Type::STK_Floating: + case Type::STK_FloatingComplex: + case Type::STK_IntegralComplex: + case Type::STK_MemberPointer: + case Type::STK_FixedPoint: + llvm_unreachable("illegal cast from pointer"); + } + llvm_unreachable("Should have returned before this"); - LHS = ImpCastExprToType(LHS.get(), ResTy, PrepareScalarCast(LHS, ResTy)); - RHS = ImpCastExprToType(RHS.get(), ResTy, PrepareScalarCast(RHS, ResTy)); - - return ResTy; - } - - // If both operands are the same structure or union type, the result is that - // type. - if (const RecordType *LHSRT = LHSTy->getAs()) { // C99 6.5.15p3 - if (const RecordType *RHSRT = RHSTy->getAs()) - if (LHSRT->getDecl() == RHSRT->getDecl()) - // "If both the operands have structure or union type, the result has - // that type." This implies that CV qualifiers are dropped. - return Context.getCommonSugaredType(LHSTy.getUnqualifiedType(), - RHSTy.getUnqualifiedType()); - // FIXME: Type of conditional expression must be complete in C mode. - } - - // C99 6.5.15p5: "If both operands have void type, the result has void type." - // The following || allows only one side to be void (a GCC-ism). - if (LHSTy->isVoidType() || RHSTy->isVoidType()) { - QualType ResTy; - if (LHSTy->isVoidType() && RHSTy->isVoidType()) { - ResTy = Context.getCommonSugaredType(LHSTy, RHSTy); - } else if (RHSTy->isVoidType()) { - ResTy = RHSTy; - Diag(RHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) - << RHS.get()->getSourceRange(); - } else { - ResTy = LHSTy; - Diag(LHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) - << LHS.get()->getSourceRange(); + case Type::STK_FixedPoint: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + return CK_FixedPointCast; + case Type::STK_Bool: + return CK_FixedPointToBoolean; + case Type::STK_Integral: + return CK_FixedPointToIntegral; + case Type::STK_Floating: + return CK_FixedPointToFloating; + case Type::STK_IntegralComplex: + case Type::STK_FloatingComplex: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << DestTy; + return CK_IntegralCast; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + case Type::STK_MemberPointer: + llvm_unreachable("illegal cast to pointer type"); } - LHS = ImpCastExprToType(LHS.get(), ResTy, CK_ToVoid); - RHS = ImpCastExprToType(RHS.get(), ResTy, CK_ToVoid); - return ResTy; - } + llvm_unreachable("Should have returned before this"); - // C23 6.5.15p7: - // ... if both the second and third operands have nullptr_t type, the - // result also has that type. - if (LHSTy->isNullPtrType() && Context.hasSameType(LHSTy, RHSTy)) - return ResTy; + case Type::STK_Bool: // casting from bool is like casting from an integer + case Type::STK_Integral: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + if (Src.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) + return CK_NullToPointer; + return CK_IntegralToPointer; + case Type::STK_Bool: + return CK_IntegralToBoolean; + case Type::STK_Integral: + return CK_IntegralCast; + case Type::STK_Floating: + return CK_IntegralToFloating; + case Type::STK_IntegralComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_IntegralCast); + return CK_IntegralRealToComplex; + case Type::STK_FloatingComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_IntegralToFloating); + return CK_FloatingRealToComplex; + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_IntegralToFixedPoint; + } + llvm_unreachable("Should have returned before this"); - // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has - // the type of the other operand." - if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; - if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; + case Type::STK_Floating: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_Floating: + return CK_FloatingCast; + case Type::STK_Bool: + return CK_FloatingToBoolean; + case Type::STK_Integral: + return CK_FloatingToIntegral; + case Type::STK_FloatingComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_FloatingCast); + return CK_FloatingRealToComplex; + case Type::STK_IntegralComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_FloatingToIntegral); + return CK_IntegralRealToComplex; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid float->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_FloatingToFixedPoint; + } + llvm_unreachable("Should have returned before this"); - // All objective-c pointer type analysis is done here. - QualType compositeType = - ObjC().FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); - if (!compositeType.isNull()) - return compositeType; - - - // Handle block pointer types. - if (LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) - return checkConditionalBlockPointerCompatibility(*this, LHS, RHS, - QuestionLoc); - - // Check constraints for C object pointers types (C99 6.5.15p3,6). - if (LHSTy->isPointerType() && RHSTy->isPointerType()) - return checkConditionalObjectPointersCompatibility(*this, LHS, RHS, - QuestionLoc); - - // GCC compatibility: soften pointer/integer mismatch. Note that - // null pointers have been filtered out by this point. - if (checkPointerIntegerMismatch(*this, LHS, RHS.get(), QuestionLoc, - /*IsIntFirstExpr=*/true)) - return RHSTy; - if (checkPointerIntegerMismatch(*this, RHS, LHS.get(), QuestionLoc, - /*IsIntFirstExpr=*/false)) - return LHSTy; - - // Emit a better diagnostic if one of the expressions is a null pointer - // constant and the other is not a pointer type. In this case, the user most - // likely forgot to take the address of the other expression. - if (DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc)) - return QualType(); - - // Finally, if the LHS and RHS types are canonically the same type, we can - // use the common sugared type. - if (Context.hasSameType(LHSTy, RHSTy)) - return Context.getCommonSugaredType(LHSTy, RHSTy); - - // Otherwise, the operands are not compatible. - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); -} + case Type::STK_FloatingComplex: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FloatingComplex: + return CK_FloatingComplexCast; + case Type::STK_IntegralComplex: + return CK_FloatingComplexToIntegralComplex; + case Type::STK_Floating: { + QualType ET = SrcTy->castAs()->getElementType(); + if (Context.hasSameType(ET, DestTy)) + return CK_FloatingComplexToReal; + Src = ImpCastExprToType(Src.get(), ET, CK_FloatingComplexToReal); + return CK_FloatingCast; + } + case Type::STK_Bool: + return CK_FloatingComplexToBoolean; + case Type::STK_Integral: + Src = ImpCastExprToType(Src.get(), + SrcTy->castAs()->getElementType(), + CK_FloatingComplexToReal); + return CK_FloatingToIntegral; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid complex float->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << SrcTy; + return CK_IntegralCast; + } + llvm_unreachable("Should have returned before this"); -/// SuggestParentheses - Emit a note with a fixit hint that wraps -/// ParenRange in parentheses. -static void SuggestParentheses(Sema &Self, SourceLocation Loc, - const PartialDiagnostic &Note, - SourceRange ParenRange) { - SourceLocation EndLoc = Self.getLocForEndOfToken(ParenRange.getEnd()); - if (ParenRange.getBegin().isFileID() && ParenRange.getEnd().isFileID() && - EndLoc.isValid()) { - Self.Diag(Loc, Note) - << FixItHint::CreateInsertion(ParenRange.getBegin(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); - } else { - // We can't display the parentheses, so just show the bare note. - Self.Diag(Loc, Note) << ParenRange; + case Type::STK_IntegralComplex: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FloatingComplex: + return CK_IntegralComplexToFloatingComplex; + case Type::STK_IntegralComplex: + return CK_IntegralComplexCast; + case Type::STK_Integral: { + QualType ET = SrcTy->castAs()->getElementType(); + if (Context.hasSameType(ET, DestTy)) + return CK_IntegralComplexToReal; + Src = ImpCastExprToType(Src.get(), ET, CK_IntegralComplexToReal); + return CK_IntegralCast; + } + case Type::STK_Bool: + return CK_IntegralComplexToBoolean; + case Type::STK_Floating: + Src = ImpCastExprToType(Src.get(), + SrcTy->castAs()->getElementType(), + CK_IntegralComplexToReal); + return CK_IntegralToFloating; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid complex int->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << SrcTy; + return CK_IntegralCast; + } + llvm_unreachable("Should have returned before this"); } -} -static bool IsArithmeticOp(BinaryOperatorKind Opc) { - return BinaryOperator::isAdditiveOp(Opc) || - BinaryOperator::isMultiplicativeOp(Opc) || - BinaryOperator::isShiftOp(Opc) || Opc == BO_And || Opc == BO_Or; - // This only checks for bitwise-or and bitwise-and, but not bitwise-xor and - // not any of the logical operators. Bitwise-xor is commonly used as a - // logical-xor because there is no logical-xor operator. The logical - // operators, including uses of xor, have a high false positive rate for - // precedence warnings. + llvm_unreachable("Unhandled scalar cast"); } -/// IsArithmeticBinaryExpr - Returns true if E is an arithmetic binary -/// expression, either using a built-in or overloaded operator, -/// and sets *OpCode to the opcode and *RHSExprs to the right-hand side -/// expression. -static bool IsArithmeticBinaryExpr(const Expr *E, BinaryOperatorKind *Opcode, - const Expr **RHSExprs) { - // Don't strip parenthesis: we should not warn if E is in parenthesis. - E = E->IgnoreImpCasts(); - E = E->IgnoreConversionOperatorSingleStep(); - E = E->IgnoreImpCasts(); - if (const auto *MTE = dyn_cast(E)) { - E = MTE->getSubExpr(); - E = E->IgnoreImpCasts(); - } - - // Built-in binary operator. - if (const auto *OP = dyn_cast(E); - OP && IsArithmeticOp(OP->getOpcode())) { - *Opcode = OP->getOpcode(); - *RHSExprs = OP->getRHS(); +static bool breakDownVectorType(QualType type, uint64_t &len, + QualType &eltType) { + // Vectors are simple. + if (const VectorType *vecType = type->getAs()) { + len = vecType->getNumElements(); + eltType = vecType->getElementType(); + assert(eltType->isScalarType()); return true; } - // Overloaded operator. - if (const auto *Call = dyn_cast(E)) { - if (Call->getNumArgs() != 2) - return false; + // We allow lax conversion to and from non-vector types, but only if + // they're real types (i.e. non-complex, non-pointer scalar types). + if (!type->isRealType()) return false; - // Make sure this is really a binary operator that is safe to pass into - // BinaryOperator::getOverloadedOpcode(), e.g. it's not a subscript op. - OverloadedOperatorKind OO = Call->getOperator(); - if (OO < OO_Plus || OO > OO_Arrow || - OO == OO_PlusPlus || OO == OO_MinusMinus) + len = 1; + eltType = type; + return true; +} + +bool Sema::isValidSveBitcast(QualType srcTy, QualType destTy) { + assert(srcTy->isVectorType() || destTy->isVectorType()); + + auto ValidScalableConversion = [](QualType FirstType, QualType SecondType) { + if (!FirstType->isSVESizelessBuiltinType()) return false; - BinaryOperatorKind OpKind = BinaryOperator::getOverloadedOpcode(OO); - if (IsArithmeticOp(OpKind)) { - *Opcode = OpKind; - *RHSExprs = Call->getArg(1); - return true; - } - } + const auto *VecTy = SecondType->getAs(); + return VecTy && VecTy->getVectorKind() == VectorKind::SveFixedLengthData; + }; - return false; + return ValidScalableConversion(srcTy, destTy) || + ValidScalableConversion(destTy, srcTy); } -/// ExprLooksBoolean - Returns true if E looks boolean, i.e. it has boolean type -/// or is a logical expression such as (x==y) which has int type, but is -/// commonly interpreted as boolean. -static bool ExprLooksBoolean(const Expr *E) { - E = E->IgnoreParenImpCasts(); +bool Sema::areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy) { + if (!destTy->isMatrixType() || !srcTy->isMatrixType()) + return false; - if (E->getType()->isBooleanType()) - return true; - if (const auto *OP = dyn_cast(E)) - return OP->isComparisonOp() || OP->isLogicalOp(); - if (const auto *OP = dyn_cast(E)) - return OP->getOpcode() == UO_LNot; - if (E->getType()->isPointerType()) - return true; - // FIXME: What about overloaded operator calls returning "unspecified boolean - // type"s (commonly pointer-to-members)? + const ConstantMatrixType *matSrcType = srcTy->getAs(); + const ConstantMatrixType *matDestType = destTy->getAs(); - return false; + return matSrcType->getNumRows() == matDestType->getNumRows() && + matSrcType->getNumColumns() == matDestType->getNumColumns(); } -/// DiagnoseConditionalPrecedence - Emit a warning when a conditional operator -/// and binary operator are mixed in a way that suggests the programmer assumed -/// the conditional operator has higher precedence, for example: -/// "int x = a + someBinaryCondition ? 1 : 2". -static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc, - Expr *Condition, const Expr *LHSExpr, - const Expr *RHSExpr) { - BinaryOperatorKind CondOpcode; - const Expr *CondRHS; +bool Sema::areVectorTypesSameSize(QualType SrcTy, QualType DestTy) { + assert(DestTy->isVectorType() || SrcTy->isVectorType()); - if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS)) - return; - if (!ExprLooksBoolean(CondRHS)) - return; + uint64_t SrcLen, DestLen; + QualType SrcEltTy, DestEltTy; + if (!breakDownVectorType(SrcTy, SrcLen, SrcEltTy)) + return false; + if (!breakDownVectorType(DestTy, DestLen, DestEltTy)) + return false; - // The condition is an arithmetic binary expression, with a right- - // hand side that looks boolean, so warn. + // ASTContext::getTypeSize will return the size rounded up to a + // power of 2, so instead of using that, we need to use the raw + // element size multiplied by the element count. + uint64_t SrcEltSize = Context.getTypeSize(SrcEltTy); + uint64_t DestEltSize = Context.getTypeSize(DestEltTy); - unsigned DiagID = BinaryOperator::isBitwiseOp(CondOpcode) - ? diag::warn_precedence_bitwise_conditional - : diag::warn_precedence_conditional; + return (SrcLen * SrcEltSize == DestLen * DestEltSize); +} - Self.Diag(OpLoc, DiagID) - << Condition->getSourceRange() - << BinaryOperator::getOpcodeStr(CondOpcode); +bool Sema::anyAltivecTypes(QualType SrcTy, QualType DestTy) { + assert((DestTy->isVectorType() || SrcTy->isVectorType()) && + "expected at least one type to be a vector here"); - SuggestParentheses( - Self, OpLoc, - Self.PDiag(diag::note_precedence_silence) - << BinaryOperator::getOpcodeStr(CondOpcode), - SourceRange(Condition->getBeginLoc(), Condition->getEndLoc())); - - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_conditional_first), - SourceRange(CondRHS->getBeginLoc(), RHSExpr->getEndLoc())); -} - -/// Compute the nullability of a conditional expression. -static QualType computeConditionalNullability(QualType ResTy, bool IsBin, - QualType LHSTy, QualType RHSTy, - ASTContext &Ctx) { - if (!ResTy->isAnyPointerType()) - return ResTy; - - auto GetNullability = [](QualType Ty) { - std::optional Kind = Ty->getNullability(); - if (Kind) { - // For our purposes, treat _Nullable_result as _Nullable. - if (*Kind == NullabilityKind::NullableResult) - return NullabilityKind::Nullable; - return *Kind; - } - return NullabilityKind::Unspecified; - }; + bool IsSrcTyAltivec = + SrcTy->isVectorType() && ((SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecVector) || + (SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecBool) || + (SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecPixel)); - auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); - NullabilityKind MergedKind; + bool IsDestTyAltivec = DestTy->isVectorType() && + ((DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecVector) || + (DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecBool) || + (DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecPixel)); - // Compute nullability of a binary conditional expression. - if (IsBin) { - if (LHSKind == NullabilityKind::NonNull) - MergedKind = NullabilityKind::NonNull; - else - MergedKind = RHSKind; - // Compute nullability of a normal conditional expression. - } else { - if (LHSKind == NullabilityKind::Nullable || - RHSKind == NullabilityKind::Nullable) - MergedKind = NullabilityKind::Nullable; - else if (LHSKind == NullabilityKind::NonNull) - MergedKind = RHSKind; - else if (RHSKind == NullabilityKind::NonNull) - MergedKind = LHSKind; - else - MergedKind = NullabilityKind::Unspecified; - } + return (IsSrcTyAltivec || IsDestTyAltivec); +} - // Return if ResTy already has the correct nullability. - if (GetNullability(ResTy) == MergedKind) - return ResTy; +bool Sema::areLaxCompatibleVectorTypes(QualType srcTy, QualType destTy) { + assert(destTy->isVectorType() || srcTy->isVectorType()); - // Strip all nullability from ResTy. - while (ResTy->getNullability()) - ResTy = ResTy.getSingleStepDesugaredType(Ctx); + // Disallow lax conversions between scalars and ExtVectors (these + // conversions are allowed for other vector types because common headers + // depend on them). Most scalar OP ExtVector cases are handled by the + // splat path anyway, which does what we want (convert, not bitcast). + // What this rules out for ExtVectors is crazy things like char4*float. + if (srcTy->isScalarType() && destTy->isExtVectorType()) return false; + if (destTy->isScalarType() && srcTy->isExtVectorType()) return false; - // Create a new AttributedType with the new nullability kind. - return Ctx.getAttributedType(MergedKind, ResTy, ResTy); + return areVectorTypesSameSize(srcTy, destTy); } -ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, - SourceLocation ColonLoc, - Expr *CondExpr, Expr *LHSExpr, - Expr *RHSExpr) { - if (!Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes in the condition because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - ExprResult CondResult = CorrectDelayedTyposInExpr(CondExpr); - ExprResult LHSResult = CorrectDelayedTyposInExpr(LHSExpr); - ExprResult RHSResult = CorrectDelayedTyposInExpr(RHSExpr); +bool Sema::isLaxVectorConversion(QualType srcTy, QualType destTy) { + assert(destTy->isVectorType() || srcTy->isVectorType()); - if (!CondResult.isUsable()) - return ExprError(); + switch (Context.getLangOpts().getLaxVectorConversions()) { + case LangOptions::LaxVectorConversionKind::None: + return false; - if (LHSExpr) { - if (!LHSResult.isUsable()) - return ExprError(); + case LangOptions::LaxVectorConversionKind::Integer: + if (!srcTy->isIntegralOrEnumerationType()) { + auto *Vec = srcTy->getAs(); + if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + return false; } + if (!destTy->isIntegralOrEnumerationType()) { + auto *Vec = destTy->getAs(); + if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + return false; + } + // OK, integer (vector) -> integer (vector) bitcast. + break; - if (!RHSResult.isUsable()) - return ExprError(); - - CondExpr = CondResult.get(); - LHSExpr = LHSResult.get(); - RHSExpr = RHSResult.get(); + case LangOptions::LaxVectorConversionKind::All: + break; } - // If this is the gnu "x ?: y" extension, analyze the types as though the LHS - // was the condition. - OpaqueValueExpr *opaqueValue = nullptr; - Expr *commonExpr = nullptr; - if (!LHSExpr) { - commonExpr = CondExpr; - // Lower out placeholder types first. This is important so that we don't - // try to capture a placeholder. This happens in few cases in C++; such - // as Objective-C++'s dictionary subscripting syntax. - if (commonExpr->hasPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(commonExpr); - if (!result.isUsable()) return ExprError(); - commonExpr = result.get(); - } - // We usually want to apply unary conversions *before* saving, except - // in the special case of a C++ l-value conditional. - if (!(getLangOpts().CPlusPlus - && !commonExpr->isTypeDependent() - && commonExpr->getValueKind() == RHSExpr->getValueKind() - && commonExpr->isGLValue() - && commonExpr->isOrdinaryOrBitFieldObject() - && RHSExpr->isOrdinaryOrBitFieldObject() - && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) { - ExprResult commonRes = UsualUnaryConversions(commonExpr); - if (commonRes.isInvalid()) - return ExprError(); - commonExpr = commonRes.get(); - } + return areLaxCompatibleVectorTypes(srcTy, destTy); +} - // If the common expression is a class or array prvalue, materialize it - // so that we can safely refer to it multiple times. - if (commonExpr->isPRValue() && (commonExpr->getType()->isRecordType() || - commonExpr->getType()->isArrayType())) { - ExprResult MatExpr = TemporaryMaterializationConversion(commonExpr); - if (MatExpr.isInvalid()) - return ExprError(); - commonExpr = MatExpr.get(); +bool Sema::CheckMatrixCast(SourceRange R, QualType DestTy, QualType SrcTy, + CastKind &Kind) { + if (SrcTy->isMatrixType() && DestTy->isMatrixType()) { + if (!areMatrixTypesOfTheSameDimension(SrcTy, DestTy)) { + return Diag(R.getBegin(), diag::err_invalid_conversion_between_matrixes) + << DestTy << SrcTy << R; } - - opaqueValue = new (Context) OpaqueValueExpr(commonExpr->getExprLoc(), - commonExpr->getType(), - commonExpr->getValueKind(), - commonExpr->getObjectKind(), - commonExpr); - LHSExpr = CondExpr = opaqueValue; + } else if (SrcTy->isMatrixType()) { + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_matrix_and_type) + << SrcTy << DestTy << R; + } else if (DestTy->isMatrixType()) { + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_matrix_and_type) + << DestTy << SrcTy << R; } - QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr; - QualType result = CheckConditionalOperands(Cond, LHS, RHS, - VK, OK, QuestionLoc); - if (result.isNull() || Cond.isInvalid() || LHS.isInvalid() || - RHS.isInvalid()) - return ExprError(); - - DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), - RHS.get()); - - CheckBoolLikeConversion(Cond.get(), QuestionLoc); + Kind = CK_MatrixCast; + return false; +} - result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, - Context); +bool Sema::CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, + CastKind &Kind) { + assert(VectorTy->isVectorType() && "Not a vector type!"); - if (!commonExpr) - return new (Context) - ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, - RHS.get(), result, VK, OK); + if (Ty->isVectorType() || Ty->isIntegralType(Context)) { + if (!areLaxCompatibleVectorTypes(Ty, VectorTy)) + return Diag(R.getBegin(), + Ty->isVectorType() ? + diag::err_invalid_conversion_between_vectors : + diag::err_invalid_conversion_between_vector_and_integer) + << VectorTy << Ty << R; + } else + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_vector_and_scalar) + << VectorTy << Ty << R; - return new (Context) BinaryConditionalOperator( - commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, - ColonLoc, result, VK, OK); + Kind = CK_BitCast; + return false; } -bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { - unsigned FromAttributes = 0, ToAttributes = 0; - if (const auto *FromFn = - dyn_cast(Context.getCanonicalType(FromType))) - FromAttributes = - FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; - if (const auto *ToFn = - dyn_cast(Context.getCanonicalType(ToType))) - ToAttributes = - ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; +ExprResult Sema::prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr) { + QualType DestElemTy = VectorTy->castAs()->getElementType(); - return FromAttributes != ToAttributes; -} + if (DestElemTy == SplattedExpr->getType()) + return SplattedExpr; -// Check if we have a conversion between incompatible cmse function pointer -// types, that is, a conversion between a function pointer with the -// cmse_nonsecure_call attribute and one without. -static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType, - QualType ToType) { - if (const auto *ToFn = - dyn_cast(S.Context.getCanonicalType(ToType))) { - if (const auto *FromFn = - dyn_cast(S.Context.getCanonicalType(FromType))) { - FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo(); - FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); + assert(DestElemTy->isFloatingType() || + DestElemTy->isIntegralOrEnumerationType()); - return ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall(); - } - } - return false; + CastKind CK; + if (VectorTy->isExtVectorType() && SplattedExpr->getType()->isBooleanType()) { + // OpenCL requires that we convert `true` boolean expressions to -1, but + // only when splatting vectors. + if (DestElemTy->isFloatingType()) { + // To avoid having to have a CK_BooleanToSignedFloating cast kind, we cast + // in two steps: boolean to signed integral, then to floating. + ExprResult CastExprRes = ImpCastExprToType(SplattedExpr, Context.IntTy, + CK_BooleanToSignedIntegral); + SplattedExpr = CastExprRes.get(); + CK = CK_IntegralToFloating; + } else { + CK = CK_BooleanToSignedIntegral; + } + } else { + ExprResult CastExprRes = SplattedExpr; + CK = PrepareScalarCast(CastExprRes, DestElemTy); + if (CastExprRes.isInvalid()) + return ExprError(); + SplattedExpr = CastExprRes.get(); + } + return ImpCastExprToType(SplattedExpr, DestElemTy, CK); } -// checkPointerTypesForAssignment - This is a very tricky routine (despite -// being closely modeled after the C99 spec:-). The odd characteristic of this -// routine is it effectively iqnores the qualifiers on the top level pointee. -// This circumvents the usual type rules specified in 6.2.7p1 & 6.7.5.[1-3]. -// FIXME: add a couple examples in this comment. -static Sema::AssignConvertType -checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, - SourceLocation Loc) { - assert(LHSType.isCanonical() && "LHS not canonicalized!"); - assert(RHSType.isCanonical() && "RHS not canonicalized!"); - - // get the "pointed to" type (ignoring qualifiers at the top level) - const Type *lhptee, *rhptee; - Qualifiers lhq, rhq; - std::tie(lhptee, lhq) = - cast(LHSType)->getPointeeType().split().asPair(); - std::tie(rhptee, rhq) = - cast(RHSType)->getPointeeType().split().asPair(); - - Sema::AssignConvertType ConvTy = Sema::Compatible; +ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, + Expr *CastExpr, CastKind &Kind) { + assert(DestTy->isExtVectorType() && "Not an extended vector type!"); - // C99 6.5.16.1p1: This following citation is common to constraints - // 3 & 4 (below). ...and the type *pointed to* by the left has all the - // qualifiers of the type *pointed to* by the right; + QualType SrcTy = CastExpr->getType(); - // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. - if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && - lhq.compatiblyIncludesObjCLifetime(rhq)) { - // Ignore lifetime for further calculation. - lhq.removeObjCLifetime(); - rhq.removeObjCLifetime(); + // If SrcTy is a VectorType, the total size must match to explicitly cast to + // an ExtVectorType. + // In OpenCL, casts between vectors of different types are not allowed. + // (See OpenCL 6.2). + if (SrcTy->isVectorType()) { + if (!areLaxCompatibleVectorTypes(SrcTy, DestTy) || + (getLangOpts().OpenCL && + !Context.hasSameUnqualifiedType(DestTy, SrcTy))) { + Diag(R.getBegin(),diag::err_invalid_conversion_between_ext_vectors) + << DestTy << SrcTy << R; + return ExprError(); + } + Kind = CK_BitCast; + return CastExpr; } - if (!lhq.compatiblyIncludes(rhq)) { - // Treat address-space mismatches as fatal. - if (!lhq.isAddressSpaceSupersetOf(rhq)) - return Sema::IncompatiblePointerDiscardsQualifiers; + // All non-pointer scalars can be cast to ExtVector type. The appropriate + // conversion will take place first from scalar to elt type, and then + // splat from elt type to vector. + if (SrcTy->isPointerType()) + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_vector_and_scalar) + << DestTy << SrcTy << R; - // It's okay to add or remove GC or lifetime qualifiers when converting to - // and from void*. - else if (lhq.withoutObjCGCAttr().withoutObjCLifetime() - .compatiblyIncludes( - rhq.withoutObjCGCAttr().withoutObjCLifetime()) - && (lhptee->isVoidType() || rhptee->isVoidType())) - ; // keep old + Kind = CK_VectorSplat; + return prepareVectorSplat(DestTy, CastExpr); +} - // Treat lifetime mismatches as fatal. - else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) - ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; +ExprResult +Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, + Declarator &D, ParsedType &Ty, + SourceLocation RParenLoc, Expr *CastExpr) { + assert(!D.isInvalidType() && (CastExpr != nullptr) && + "ActOnCastExpr(): missing type or expr"); - // Treat pointer-auth mismatches as fatal. - else if (lhq.getPointerAuth() != rhq.getPointerAuth()) - ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + TypeSourceInfo *castTInfo = GetTypeForDeclaratorCast(D, CastExpr->getType()); + if (D.isInvalidType()) + return ExprError(); - // For GCC/MS compatibility, other qualifier mismatches are treated - // as still compatible in C. - else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; + if (getLangOpts().CPlusPlus) { + // Check that there are no default arguments (C++ only). + CheckExtraCXXDefaultArguments(D); + } else { + // Make sure any TypoExprs have been dealt with. + ExprResult Res = CorrectDelayedTyposInExpr(CastExpr); + if (!Res.isUsable()) + return ExprError(); + CastExpr = Res.get(); } - // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or - // incomplete type and the other is a pointer to a qualified or unqualified - // version of void... - if (lhptee->isVoidType()) { - if (rhptee->isIncompleteOrObjectType()) - return ConvTy; + checkUnusedDeclAttributes(D); - // As an extension, we allow cast to/from void* to function pointer. - assert(rhptee->isFunctionType()); - return Sema::FunctionVoidPointer; + QualType castType = castTInfo->getType(); + Ty = CreateParsedType(castType, castTInfo); + + bool isVectorLiteral = false; + + // Check for an altivec or OpenCL literal, + // i.e. all the elements are integer constants. + ParenExpr *PE = dyn_cast(CastExpr); + ParenListExpr *PLE = dyn_cast(CastExpr); + if ((getLangOpts().AltiVec || getLangOpts().ZVector || getLangOpts().OpenCL) + && castType->isVectorType() && (PE || PLE)) { + if (PLE && PLE->getNumExprs() == 0) { + Diag(PLE->getExprLoc(), diag::err_altivec_empty_initializer); + return ExprError(); + } + if (PE || PLE->getNumExprs() == 1) { + Expr *E = (PE ? PE->getSubExpr() : PLE->getExpr(0)); + if (!E->isTypeDependent() && !E->getType()->isVectorType()) + isVectorLiteral = true; + } + else + isVectorLiteral = true; } - if (rhptee->isVoidType()) { - if (lhptee->isIncompleteOrObjectType()) - return ConvTy; + // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' + // then handle it as such. + if (isVectorLiteral) + return BuildVectorLiteral(LParenLoc, RParenLoc, CastExpr, castTInfo); - // As an extension, we allow cast to/from void* to function pointer. - assert(lhptee->isFunctionType()); - return Sema::FunctionVoidPointer; + // If the Expr being casted is a ParenListExpr, handle it specially. + // This is not an AltiVec-style cast, so turn the ParenListExpr into a + // sequence of BinOp comma operators. + if (isa(CastExpr)) { + ExprResult Result = MaybeConvertParenListExprToParenExpr(S, CastExpr); + if (Result.isInvalid()) return ExprError(); + CastExpr = Result.get(); } - if (!S.Diags.isIgnored( - diag::warn_typecheck_convert_incompatible_function_pointer_strict, - Loc) && - RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType() && - !S.IsFunctionConversion(RHSType, LHSType, RHSType)) - return Sema::IncompatibleFunctionPointerStrict; + if (getLangOpts().CPlusPlus && !castType->isVoidType()) + Diag(LParenLoc, diag::warn_old_style_cast) << CastExpr->getSourceRange(); - // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or - // unqualified versions of compatible types, ... - QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); - if (!S.Context.typesAreCompatible(ltrans, rtrans)) { - // Check if the pointee types are compatible ignoring the sign. - // We explicitly check for char so that we catch "char" vs - // "unsigned char" on systems where "char" is unsigned. - if (lhptee->isCharType()) - ltrans = S.Context.UnsignedCharTy; - else if (lhptee->hasSignedIntegerRepresentation()) - ltrans = S.Context.getCorrespondingUnsignedType(ltrans); + ObjC().CheckTollFreeBridgeCast(castType, CastExpr); - if (rhptee->isCharType()) - rtrans = S.Context.UnsignedCharTy; - else if (rhptee->hasSignedIntegerRepresentation()) - rtrans = S.Context.getCorrespondingUnsignedType(rtrans); + ObjC().CheckObjCBridgeRelatedCast(castType, CastExpr); - if (ltrans == rtrans) { - // Types are compatible ignoring the sign. Qualifier incompatibility - // takes priority over sign incompatibility because the sign - // warning can be disabled. - if (ConvTy != Sema::Compatible) - return ConvTy; + DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr); - return Sema::IncompatiblePointerSign; - } + return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr); +} - // If we are a multi-level pointer, it's possible that our issue is simply - // one of qualification - e.g. char ** -> const char ** is not allowed. If - // the eventual target type is the same and the pointers have the same - // level of indirection, this must be the issue. - if (isa(lhptee) && isa(rhptee)) { - do { - std::tie(lhptee, lhq) = - cast(lhptee)->getPointeeType().split().asPair(); - std::tie(rhptee, rhq) = - cast(rhptee)->getPointeeType().split().asPair(); +ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, + SourceLocation RParenLoc, Expr *E, + TypeSourceInfo *TInfo) { + assert((isa(E) || isa(E)) && + "Expected paren or paren list expression"); - // Inconsistent address spaces at this point is invalid, even if the - // address spaces would be compatible. - // FIXME: This doesn't catch address space mismatches for pointers of - // different nesting levels, like: - // __local int *** a; - // int ** b = a; - // It's not clear how to actually determine when such pointers are - // invalidly incompatible. - if (lhq.getAddressSpace() != rhq.getAddressSpace()) - return Sema::IncompatibleNestedPointerAddressSpaceMismatch; + Expr **exprs; + unsigned numExprs; + Expr *subExpr; + SourceLocation LiteralLParenLoc, LiteralRParenLoc; + if (ParenListExpr *PE = dyn_cast(E)) { + LiteralLParenLoc = PE->getLParenLoc(); + LiteralRParenLoc = PE->getRParenLoc(); + exprs = PE->getExprs(); + numExprs = PE->getNumExprs(); + } else { // isa by assertion at function entrance + LiteralLParenLoc = cast(E)->getLParen(); + LiteralRParenLoc = cast(E)->getRParen(); + subExpr = cast(E)->getSubExpr(); + exprs = &subExpr; + numExprs = 1; + } - } while (isa(lhptee) && isa(rhptee)); + QualType Ty = TInfo->getType(); + assert(Ty->isVectorType() && "Expected vector type"); - if (lhptee == rhptee) - return Sema::IncompatibleNestedPointerQualifiers; - } + SmallVector initExprs; + const VectorType *VTy = Ty->castAs(); + unsigned numElems = VTy->getNumElements(); - // General pointer incompatibility takes priority over qualifiers. - if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType()) - return Sema::IncompatibleFunctionPointer; - return Sema::IncompatiblePointer; - } - if (!S.getLangOpts().CPlusPlus && - S.IsFunctionConversion(ltrans, rtrans, ltrans)) - return Sema::IncompatibleFunctionPointer; - if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) - return Sema::IncompatibleFunctionPointer; - if (S.IsInvalidSMECallConversion(rtrans, ltrans)) - return Sema::IncompatibleFunctionPointer; - return ConvTy; + // '(...)' form of vector initialization in AltiVec: the number of + // initializers must be one or must match the size of the vector. + // If a single value is specified in the initializer then it will be + // replicated to all the components of the vector + if (CheckAltivecInitFromScalar(E->getSourceRange(), Ty, + VTy->getElementType())) + return ExprError(); + if (ShouldSplatAltivecScalarInCast(VTy)) { + // The number of initializers must be one or must match the size of the + // vector. If a single value is specified in the initializer then it will + // be replicated to all the components of the vector + if (numExprs == 1) { + QualType ElemTy = VTy->getElementType(); + ExprResult Literal = DefaultLvalueConversion(exprs[0]); + if (Literal.isInvalid()) + return ExprError(); + Literal = ImpCastExprToType(Literal.get(), ElemTy, + PrepareScalarCast(Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); + } + else if (numExprs < numElems) { + Diag(E->getExprLoc(), + diag::err_incorrect_number_of_vector_initializers); + return ExprError(); + } + else + initExprs.append(exprs, exprs + numExprs); + } + else { + // For OpenCL, when the number of initializers is a single value, + // it will be replicated to all components of the vector. + if (getLangOpts().OpenCL && VTy->getVectorKind() == VectorKind::Generic && + numExprs == 1) { + QualType ElemTy = VTy->getElementType(); + ExprResult Literal = DefaultLvalueConversion(exprs[0]); + if (Literal.isInvalid()) + return ExprError(); + Literal = ImpCastExprToType(Literal.get(), ElemTy, + PrepareScalarCast(Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); + } + + initExprs.append(exprs, exprs + numExprs); + } + // FIXME: This means that pretty-printing the final AST will produce curly + // braces instead of the original commas. + InitListExpr *initE = new (Context) InitListExpr(Context, LiteralLParenLoc, + initExprs, LiteralRParenLoc); + initE->setType(Ty); + return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE); } -/// checkBlockPointerTypesForAssignment - This routine determines whether two -/// block pointer types are compatible or whether a block and normal pointer -/// are compatible. It is more restrict than comparing two function pointer -// types. -static Sema::AssignConvertType -checkBlockPointerTypesForAssignment(Sema &S, QualType LHSType, - QualType RHSType) { - assert(LHSType.isCanonical() && "LHS not canonicalized!"); - assert(RHSType.isCanonical() && "RHS not canonicalized!"); +ExprResult +Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) { + ParenListExpr *E = dyn_cast(OrigExpr); + if (!E) + return OrigExpr; - QualType lhptee, rhptee; + ExprResult Result(E->getExpr(0)); - // get the "pointed to" type (ignoring qualifiers at the top level) - lhptee = cast(LHSType)->getPointeeType(); - rhptee = cast(RHSType)->getPointeeType(); + for (unsigned i = 1, e = E->getNumExprs(); i != e && !Result.isInvalid(); ++i) + Result = ActOnBinOp(S, E->getExprLoc(), tok::comma, Result.get(), + E->getExpr(i)); - // In C++, the types have to match exactly. - if (S.getLangOpts().CPlusPlus) - return Sema::IncompatibleBlockPointer; + if (Result.isInvalid()) return ExprError(); - Sema::AssignConvertType ConvTy = Sema::Compatible; + return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get()); +} - // For blocks we enforce that qualifiers are identical. - Qualifiers LQuals = lhptee.getLocalQualifiers(); - Qualifiers RQuals = rhptee.getLocalQualifiers(); - if (S.getLangOpts().OpenCL) { - LQuals.removeAddressSpace(); - RQuals.removeAddressSpace(); +ExprResult Sema::ActOnParenListExpr(SourceLocation L, + SourceLocation R, + MultiExprArg Val) { + return ParenListExpr::Create(Context, L, Val, R); +} + +bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr, + SourceLocation QuestionLoc) { + const Expr *NullExpr = LHSExpr; + const Expr *NonPointerExpr = RHSExpr; + Expr::NullPointerConstantKind NullKind = + NullExpr->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull); + + if (NullKind == Expr::NPCK_NotNull) { + NullExpr = RHSExpr; + NonPointerExpr = LHSExpr; + NullKind = + NullExpr->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull); } - if (LQuals != RQuals) - ConvTy = Sema::CompatiblePointerDiscardsQualifiers; - // FIXME: OpenCL doesn't define the exact compile time semantics for a block - // assignment. - // The current behavior is similar to C++ lambdas. A block might be - // assigned to a variable iff its return type and parameters are compatible - // (C99 6.2.7) with the corresponding return type and parameters of the LHS of - // an assignment. Presumably it should behave in way that a function pointer - // assignment does in C, so for each parameter and return type: - // * CVR and address space of LHS should be a superset of CVR and address - // space of RHS. - // * unqualified types should be compatible. - if (S.getLangOpts().OpenCL) { - if (!S.Context.typesAreBlockPointerCompatible( - S.Context.getQualifiedType(LHSType.getUnqualifiedType(), LQuals), - S.Context.getQualifiedType(RHSType.getUnqualifiedType(), RQuals))) - return Sema::IncompatibleBlockPointer; - } else if (!S.Context.typesAreBlockPointerCompatible(LHSType, RHSType)) - return Sema::IncompatibleBlockPointer; + if (NullKind == Expr::NPCK_NotNull) + return false; - return ConvTy; + if (NullKind == Expr::NPCK_ZeroExpression) + return false; + + if (NullKind == Expr::NPCK_ZeroLiteral) { + // In this case, check to make sure that we got here from a "NULL" + // string in the source code. + NullExpr = NullExpr->IgnoreParenImpCasts(); + SourceLocation loc = NullExpr->getExprLoc(); + if (!findMacroSpelling(loc, "NULL")) + return false; + } + + int DiagType = (NullKind == Expr::NPCK_CXX11_nullptr); + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null) + << NonPointerExpr->getType() << DiagType + << NonPointerExpr->getSourceRange(); + return true; } -/// checkObjCPointerTypesForAssignment - Compares two objective-c pointer types -/// for assignment compatibility. -static Sema::AssignConvertType -checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, - QualType RHSType) { - assert(LHSType.isCanonical() && "LHS was not canonicalized!"); - assert(RHSType.isCanonical() && "RHS was not canonicalized!"); +/// Return false if the condition expression is valid, true otherwise. +static bool checkCondition(Sema &S, const Expr *Cond, + SourceLocation QuestionLoc) { + QualType CondTy = Cond->getType(); - if (LHSType->isObjCBuiltinType()) { - // Class is not compatible with ObjC object pointers. - if (LHSType->isObjCClassType() && !RHSType->isObjCBuiltinType() && - !RHSType->isObjCQualifiedClassType()) - return Sema::IncompatiblePointer; - return Sema::Compatible; - } - if (RHSType->isObjCBuiltinType()) { - if (RHSType->isObjCClassType() && !LHSType->isObjCBuiltinType() && - !LHSType->isObjCQualifiedClassType()) - return Sema::IncompatiblePointer; - return Sema::Compatible; + // OpenCL v1.1 s6.3.i says the condition cannot be a floating point type. + if (S.getLangOpts().OpenCL && CondTy->isFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << CondTy << Cond->getSourceRange(); + return true; } - QualType lhptee = LHSType->castAs()->getPointeeType(); - QualType rhptee = RHSType->castAs()->getPointeeType(); - if (!lhptee.isAtLeastAsQualifiedAs(rhptee) && - // make an exception for id

- !LHSType->isObjCQualifiedIdType()) - return Sema::CompatiblePointerDiscardsQualifiers; + // C99 6.5.15p2 + if (CondTy->isScalarType()) return false; - if (S.Context.typesAreCompatible(LHSType, RHSType)) - return Sema::Compatible; - if (LHSType->isObjCQualifiedIdType() || RHSType->isObjCQualifiedIdType()) - return Sema::IncompatibleObjCQualifiedId; - return Sema::IncompatiblePointer; + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) + << CondTy << Cond->getSourceRange(); + return true; } -Sema::AssignConvertType -Sema::CheckAssignmentConstraints(SourceLocation Loc, - QualType LHSType, QualType RHSType) { - // Fake up an opaque expression. We don't actually care about what - // cast operations are required, so if CheckAssignmentConstraints - // adds casts to this they'll be wasted, but fortunately that doesn't - // usually happen on valid code. - OpaqueValueExpr RHSExpr(Loc, RHSType, VK_PRValue); - ExprResult RHSPtr = &RHSExpr; - CastKind K; - - return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false); -} +/// Return false if the NullExpr can be promoted to PointerTy, +/// true otherwise. +static bool checkConditionalNullPointer(Sema &S, ExprResult &NullExpr, + QualType PointerTy) { + if ((!PointerTy->isAnyPointerType() && !PointerTy->isBlockPointerType()) || + !NullExpr.get()->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) + return true; -/// This helper function returns true if QT is a vector type that has element -/// type ElementType. -static bool isVector(QualType QT, QualType ElementType) { - if (const VectorType *VT = QT->getAs()) - return VT->getElementType().getCanonicalType() == ElementType; + NullExpr = S.ImpCastExprToType(NullExpr.get(), PointerTy, CK_NullToPointer); return false; } -/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently -/// has code to accommodate several GCC extensions when type checking -/// pointers. Here are some objectionable examples that GCC considers warnings: -/// -/// int a, *pint; -/// short *pshort; -/// struct foo *pfoo; -/// -/// pint = pshort; // warning: assignment from incompatible pointer type -/// a = pint; // warning: assignment makes integer from pointer without a cast -/// pint = a; // warning: assignment makes pointer from integer without a cast -/// pint = pfoo; // warning: assignment from incompatible pointer type -/// -/// As a result, the code for dealing with pointers is more complex than the -/// C99 spec dictates. -/// -/// Sets 'Kind' for any result kind except Incompatible. -Sema::AssignConvertType -Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, - CastKind &Kind, bool ConvertRHS) { - QualType RHSType = RHS.get()->getType(); - QualType OrigLHSType = LHSType; - - // Get canonical types. We're not formatting these types, just comparing - // them. - LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); - RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType(); +/// Checks compatibility between two pointers and return the resulting +/// type. +static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - // Common case: no conversion required. - if (LHSType == RHSType) { - Kind = CK_NoOp; - return Compatible; - } - - // If the LHS has an __auto_type, there are no additional type constraints - // to be worried about. - if (const auto *AT = dyn_cast(LHSType)) { - if (AT->isGNUAutoType()) { - Kind = CK_NoOp; - return Compatible; + if (S.Context.hasSameType(LHSTy, RHSTy)) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.Context.getLangOpts().BoundsSafety) { + auto MergeResult = S.Context.canMergeTypeBounds(LHSTy, RHSTy); + if (MergeResult != ASTContext::BSPTMK_CanMerge) { + S.Diag( + Loc, + diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LHSTy << RHSTy << MergeResult << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Two identical pointers types are always compatible. + return S.Context.getCommonSugaredType(LHSTy, RHSTy); } - // If we have an atomic type, try a non-atomic assignment, then just add an - // atomic qualification step. - if (const AtomicType *AtomicTy = dyn_cast(LHSType)) { - Sema::AssignConvertType result = - CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); - if (result != Compatible) - return result; - if (Kind != CK_NoOp && ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind); - Kind = CK_NonAtomicToAtomic; - return Compatible; + QualType lhptee, rhptee; + + BoundsSafetyPointerAttributes CompositeFPAttr; + + // Get the pointee types. + bool IsBlockPointer = false; + if (const BlockPointerType *LHSBTy = LHSTy->getAs()) { + lhptee = LHSBTy->getPointeeType(); + rhptee = RHSTy->castAs()->getPointeeType(); + IsBlockPointer = true; + } else { + lhptee = LHSTy->castAs()->getPointeeType(); + rhptee = RHSTy->castAs()->getPointeeType(); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: If LHS and RHS have different -fbounds-safety pointer attributes, + // take the attribute with higher precedence and convert the other into it. + // The bounds-only attributes have precedence in the following order: + // 1) __unsafe_indexable or unspecified + // 2) __bidi_indexable + // 3) __indexable + // 4) __single + // The operands are considered incompatible if an operand has unsafe pointer + // type and the other has bounds. + auto lFPAttr = LHSTy->castAs()->getPointerAttributes(); + auto rFPAttr = RHSTy->castAs()->getPointerAttributes(); + CompositeFPAttr = BoundsSafetyPointerAttributes::merge(lFPAttr, rFPAttr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ } - // If the left-hand side is a reference type, then we are in a - // (rare!) case where we've allowed the use of references in C, - // e.g., as a parameter type in a built-in function. In this case, - // just make sure that the type referenced is compatible with the - // right-hand side type. The caller is responsible for adjusting - // LHSType so that the resulting expression does not have reference + // C99 6.5.15p6: If both operands are pointers to compatible types or to + // differently qualified versions of compatible types, the result type is + // a pointer to an appropriately qualified version of the composite // type. - if (const ReferenceType *LHSTypeRef = LHSType->getAs()) { - if (Context.typesAreCompatible(LHSTypeRef->getPointeeType(), RHSType)) { - Kind = CK_LValueBitCast; - return Compatible; - } - return Incompatible; + + // Only CVR-qualifiers exist in the standard, and the differently-qualified + // clause doesn't make sense for our extensions. E.g. address space 2 should + // be incompatible with address space 3: they may live on different devices or + // anything. + Qualifiers lhQual = lhptee.getQualifiers(); + Qualifiers rhQual = rhptee.getQualifiers(); + + LangAS ResultAddrSpace = LangAS::Default; + LangAS LAddrSpace = lhQual.getAddressSpace(); + LangAS RAddrSpace = rhQual.getAddressSpace(); + + // OpenCL v1.1 s6.5 - Conversion between pointers to distinct address + // spaces is disallowed. + if (lhQual.isAddressSpaceSupersetOf(rhQual)) + ResultAddrSpace = LAddrSpace; + else if (rhQual.isAddressSpaceSupersetOf(lhQual)) + ResultAddrSpace = RAddrSpace; + else { + S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSTy << RHSTy << 2 << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - // Allow scalar to ExtVector assignments, and assignments of an ExtVector type - // to the same ExtVector type. - if (LHSType->isExtVectorType()) { - if (RHSType->isExtVectorType()) - return Incompatible; - if (RHSType->isArithmeticType()) { - // CK_VectorSplat does T -> vector T, so first cast to the element type. - if (ConvertRHS) - RHS = prepareVectorSplat(LHSType, RHS.get()); - Kind = CK_VectorSplat; - return Compatible; - } + unsigned MergedCVRQual = lhQual.getCVRQualifiers() | rhQual.getCVRQualifiers(); + auto LHSCastKind = CK_BitCast, RHSCastKind = CK_BitCast; + lhQual.removeCVRQualifiers(); + rhQual.removeCVRQualifiers(); + + if (lhQual.getPointerAuth() != rhQual.getPointerAuth()) { + S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) + << LHSTy << RHSTy + << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - // Conversions to or from vector type. - if (LHSType->isVectorType() || RHSType->isVectorType()) { - if (LHSType->isVectorType() && RHSType->isVectorType()) { - // Allow assignments of an AltiVec vector type to an equivalent GCC - // vector type and vice versa - if (Context.areCompatibleVectorTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } + // OpenCL v2.0 specification doesn't extend compatibility of type qualifiers + // (C99 6.7.3) for address spaces. We assume that the check should behave in + // the same manner as it's defined for CVR qualifiers, so for OpenCL two + // qual types are compatible iff + // * corresponded types are compatible + // * CVR qualifiers are equal + // * address spaces are equal + // Thus for conditional operator we merge CVR and address space unqualified + // pointees and if there is a composite type we return a pointer to it with + // merged qualifiers. + LHSCastKind = + LAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; + RHSCastKind = + RAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; + lhQual.removeAddressSpace(); + rhQual.removeAddressSpace(); - // If we are allowing lax vector conversions, and LHS and RHS are both - // vectors, the total size only needs to be the same. This is a bitcast; - // no bits are changed but the result type is different. - if (isLaxVectorConversion(RHSType, LHSType)) { - // The default for lax vector conversions with Altivec vectors will - // change, so if we are converting between vector types where - // at least one is an Altivec vector, emit a warning. - if (Context.getTargetInfo().getTriple().isPPC() && - anyAltivecTypes(RHSType, LHSType) && - !Context.areCompatibleVectorTypes(RHSType, LHSType)) - Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) - << RHSType << LHSType; - Kind = CK_BitCast; - return IncompatibleVectors; - } - } + lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual); + rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual); - // When the RHS comes from another lax conversion (e.g. binops between - // scalars and vectors) the result is canonicalized as a vector. When the - // LHS is also a vector, the lax is allowed by the condition above. Handle - // the case where LHS is a scalar. - if (LHSType->isScalarType()) { - const VectorType *VecType = RHSType->getAs(); - if (VecType && VecType->getNumElements() == 1 && - isLaxVectorConversion(RHSType, LHSType)) { - if (Context.getTargetInfo().getTriple().isPPC() && - (VecType->getVectorKind() == VectorKind::AltiVecVector || - VecType->getVectorKind() == VectorKind::AltiVecBool || - VecType->getVectorKind() == VectorKind::AltiVecPixel)) - Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) - << RHSType << LHSType; - ExprResult *VecExpr = &RHS; - *VecExpr = ImpCastExprToType(VecExpr->get(), LHSType, CK_BitCast); - Kind = CK_BitCast; - return Compatible; - } - } + QualType CompositeTy = S.Context.mergeTypes( + lhptee, rhptee, /*OfBlockPointer=*/false, /*Unqualified=*/false, + /*BlockReturnType=*/false, /*IsConditionalOperator=*/true); - // Allow assignments between fixed-length and sizeless SVE vectors. - if ((LHSType->isSVESizelessBuiltinType() && RHSType->isVectorType()) || - (LHSType->isVectorType() && RHSType->isSVESizelessBuiltinType())) - if (Context.areCompatibleSveTypes(LHSType, RHSType) || - Context.areLaxCompatibleSveTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } + if (CompositeTy.isNull()) { + // In this situation, we assume void* type. No especially good + // reason, but this is what gcc does, and we do have to pick + // to get a consistent AST. + QualType incompatTy; + incompatTy = S.Context.getPointerType( + S.Context.getAddrSpaceQualType(S.Context.VoidTy, ResultAddrSpace), + CompositeFPAttr); - // Allow assignments between fixed-length and sizeless RVV vectors. - if ((LHSType->isRVVSizelessBuiltinType() && RHSType->isVectorType()) || - (LHSType->isVectorType() && RHSType->isRVVSizelessBuiltinType())) { - if (Context.areCompatibleRVVTypes(LHSType, RHSType) || - Context.areLaxCompatibleRVVTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } + LHS = S.ImpCastExprToType(LHS.get(), incompatTy, LHSCastKind); + RHS = S.ImpCastExprToType(RHS.get(), incompatTy, RHSCastKind); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety) { + // Although -fbounds-safety makes no type safety guarantees, + // this is a likely footgun and is troublesome when comparing + // terminator values for __terminated_by of e.g. signed and unsigned. + S.Diag(Loc, diag::err_typecheck_cond_incompatible_pointers) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } + /*TO_UPSTREAM(BoundsSafety) OFF*/ - return Incompatible; + // FIXME: For OpenCL the warning emission and cast to void* leaves a room + // for casts between types with incompatible address space qualifiers. + // For the following code the compiler produces casts between global and + // local address spaces of the corresponded innermost pointees: + // local int *global *a; + // global int *global *b; + // a = (0 ? a : b); // see C99 6.5.16.1.p1. + S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + + return incompatTy; } - // Diagnose attempts to convert between __ibm128, __float128 and long double - // where such conversions currently can't be handled. - if (unsupportedTypeConversion(*this, LHSType, RHSType)) - return Incompatible; + // The pointer types are compatible. + // In case of OpenCL ResultTy should have the address space qualifier + // which is a superset of address spaces of both the 2nd and the 3rd + // operands of the conditional operator. + QualType ResultTy = [&, ResultAddrSpace]() { + if (S.getLangOpts().OpenCL) { + Qualifiers CompositeQuals = CompositeTy.getQualifiers(); + CompositeQuals.setAddressSpace(ResultAddrSpace); + return S.Context + .getQualifiedType(CompositeTy.getUnqualifiedType(), CompositeQuals) + .withCVRQualifiers(MergedCVRQual); + } + return CompositeTy.withCVRQualifiers(MergedCVRQual); + }(); + if (IsBlockPointer) + ResultTy = S.Context.getBlockPointerType(ResultTy); + else + ResultTy = S.Context.getPointerType(ResultTy, CompositeFPAttr); - // Disallow assigning a _Complex to a real type in C++ mode since it simply - // discards the imaginary part. - if (getLangOpts().CPlusPlus && RHSType->getAs() && - !LHSType->getAs()) - return Incompatible; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.Context.checkTerminatedByMismatch(LHSTy, RHSTy) != + ASTContext::BSPTMK_CanMerge) { + S.Diag(Loc, diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LHSTy << RHSTy << ASTContext::BSPTMK_TerminatedByMismatch + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - // Arithmetic conversions. - if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && - !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { - if (ConvertRHS) - Kind = PrepareScalarCast(RHS, LHSType); - return Compatible; + if (auto LVTTy = LHSTy->getAs()) { + assert(RHSTy->getAs()); + ResultTy = + S.Context.getValueTerminatedType(ResultTy, LVTTy->getTerminatorExpr()); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // Conversions to normal pointers. - if (const PointerType *LHSPointer = dyn_cast(LHSType)) { - // U* -> T* - if (isa(RHSType)) { - LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); - LangAS AddrSpaceR = RHSType->getPointeeType().getAddressSpace(); - if (AddrSpaceL != AddrSpaceR) - Kind = CK_AddressSpaceConversion; - else if (Context.hasCvrSimilarType(RHSType, LHSType)) - Kind = CK_NoOp; - else - Kind = CK_BitCast; - return checkPointerTypesForAssignment(*this, LHSType, RHSType, - RHS.get()->getBeginLoc()); - } + LHS = S.ImpCastExprToType(LHS.get(), ResultTy, LHSCastKind); + RHS = S.ImpCastExprToType(RHS.get(), ResultTy, RHSCastKind); + return ResultTy; +} - // int -> T* - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null? - return IntToPointer; - } +/// Return the resulting type when the operands are both block pointers. +static QualType checkConditionalBlockPointerCompatibility(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - // C pointers are not compatible with ObjC object pointers, - // with two exceptions: - if (isa(RHSType)) { - // - conversions to void* - if (LHSPointer->getPointeeType()->isVoidType()) { - Kind = CK_BitCast; - return Compatible; - } + if (!LHSTy->isBlockPointerType() || !RHSTy->isBlockPointerType()) { + if (LHSTy->isVoidPointerType() || RHSTy->isVoidPointerType()) { + QualType destType = S.Context.getPointerType(S.Context.VoidTy); + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); + return destType; + } + S.Diag(Loc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - // - conversions from 'Class' to the redefinition type - if (RHSType->isObjCClassType() && - Context.hasSameType(LHSType, - Context.getObjCClassRedefinitionType())) { - Kind = CK_BitCast; - return Compatible; - } + // We have 2 block pointer types. + return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); +} - Kind = CK_BitCast; - return IncompatiblePointer; - } +static bool haveCommonTerminator(const ASTContext &Ctx, const Expr *LHS, + const Expr *RHS) { + assert(LHS->getType()->isValueTerminatedType() || + RHS->getType()->isValueTerminatedType()); + Expr::EvalResult LHSRes; + if (!LHS->tryEvaluateTerminatorElement(LHSRes, Ctx) || !LHSRes.Val.isInt()) + return false; - // U^ -> void* - if (RHSType->getAs()) { - if (LHSPointer->getPointeeType()->isVoidType()) { - LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); - LangAS AddrSpaceR = RHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - Kind = - AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; - return Compatible; - } - } + Expr::EvalResult RHSRes; + if (!RHS->tryEvaluateTerminatorElement(RHSRes, Ctx) || !RHSRes.Val.isInt()) + return false; - return Incompatible; - } + return llvm::APSInt::isSameValue(LHSRes.Val.getInt(), RHSRes.Val.getInt()); +} - // Conversions to block pointers. - if (isa(LHSType)) { - // U^ -> T^ - if (RHSType->isBlockPointerType()) { - LangAS AddrSpaceL = LHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - LangAS AddrSpaceR = RHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; - return checkBlockPointerTypesForAssignment(*this, LHSType, RHSType); - } +/// Return the resulting type when the operands are both pointers. +static QualType +checkConditionalObjectPointersCompatibility(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + // get the pointer types + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - // int or null -> T^ - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null - return IntToBlockPointer; - } + // get the "pointed to" types + QualType lhptee = LHSTy->castAs()->getPointeeType(); + QualType rhptee = RHSTy->castAs()->getPointeeType(); - // id -> T^ - if (getLangOpts().ObjC && RHSType->isObjCIdType()) { - Kind = CK_AnyPointerToBlockPointerCast; - return Compatible; + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyPointerAttributes destFPAttr; + // Do -fbounds-safety pointer conversions ahead of any other conversions; otherwise + // we risk doing a BitCast across pointer attributes, which is bad. + BoundsSafetyPointerAttributes lFPAttr = + LHSTy->castAs()->getPointerAttributes(); + BoundsSafetyPointerAttributes rFPAttr = + RHSTy->castAs()->getPointerAttributes(); + destFPAttr = BoundsSafetyPointerAttributes::merge(lFPAttr, rFPAttr); + + // If either type is value terminated, the other also needs to be, meaning + // they will both be __single pointers. A VTT and a non-single pointer is + // always a mismatch. Exception: if a constant array or string literal + // can be evaluated and is terminated with the right value. + if (LHSTy->isValueTerminatedType() != RHSTy->isValueTerminatedType()) { + destFPAttr.setSingle(); + } + + if (lFPAttr != destFPAttr) { + QualType destType = S.Context.getPointerType(lhptee, destFPAttr); + SplitQualType Split = LHSTy.getSplitUnqualifiedType(); + destType = S.Context.getQualifiedType(destType, Split.Quals); + + if (auto RHSVTTy = RHSTy->getAs()) { + // The other type is value terminated. Try to cast this to VTT also. + destType = S.Context.getValueTerminatedType(destType, + RHSVTTy->getTerminatorExpr()); + if (!haveCommonTerminator(S.Context, LHS.get(), RHS.get())) { + const auto *DstPointerType = destType->getAs(); + int SelectIsNullTerm = + DstPointerType->getTerminatorValue(S.getASTContext()).isZero() + ? /*null_terminated*/ 1 + : /*terminated_by*/ 0; + S.Diag( + Loc, + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by) + << LHSTy << destType << /*converting*/ 3 + << LHS.get()->getSourceRange() << SelectIsNullTerm; + S.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(LHS.get(), + destType); + S.TryFixAssigningBidiIndexableExprToNullTerminated(LHS.get(), destType); + return QualType(); + } } - - // void* -> T^ - if (const PointerType *RHSPT = RHSType->getAs()) - if (RHSPT->getPointeeType()->isVoidType()) { - Kind = CK_AnyPointerToBlockPointerCast; - return Compatible; + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BoundsSafetyPointerCast); + } + + if (rFPAttr != destFPAttr) { + QualType destType = S.Context.getPointerType(rhptee, destFPAttr); + SplitQualType Split = RHSTy.getSplitUnqualifiedType(); + destType = S.Context.getQualifiedType(destType, Split.Quals); + + if (auto LHSVTTy = LHSTy->getAs()) { + // The other type is value terminated. Try to cast this to VTT also. + destType = S.Context.getValueTerminatedType(destType, + LHSVTTy->getTerminatorExpr()); + if (!haveCommonTerminator(S.Context, LHS.get(), RHS.get())) { + const auto *DstPointerType = destType->getAs(); + int SelectIsNullTerm = + DstPointerType->getTerminatorValue(S.getASTContext()).isZero() + ? /*null_terminated*/ 1 + : /*terminated_by*/ 0; + S.Diag( + Loc, + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by) + << RHSTy << destType << /*converting*/ 3 + << RHS.get()->getSourceRange() << SelectIsNullTerm; + + S.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(RHS.get(), destType); + S.TryFixAssigningBidiIndexableExprToNullTerminated(RHS.get(), destType); + return QualType(); } - - return Incompatible; + } + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BoundsSafetyPointerCast); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // Conversions to Objective-C pointers. - if (isa(LHSType)) { - // A* -> B* - if (RHSType->isObjCObjectPointerType()) { - Kind = CK_BitCast; - Sema::AssignConvertType result = - checkObjCPointerTypesForAssignment(*this, LHSType, RHSType); - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - result == Compatible && - !ObjC().CheckObjCARCUnavailableWeakConversion(OrigLHSType, RHSType)) - result = IncompatibleObjCWeakRef; - return result; - } + // ignore qualifiers on void (C99 6.5.15p3, clause 6) + if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { + // Figure out necessary qualifiers (C99 6.5.15p6) + QualType destPointee + = S.Context.getQualifiedType(lhptee, rhptee.getQualifiers()); + QualType destType = S.Context.getPointerType(destPointee, destFPAttr); + // Add qualifiers or pointer attributes if necessary. + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_NoOp); + // Promote to void*. + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); + return destType; + } + if (rhptee->isVoidType() && lhptee->isIncompleteOrObjectType()) { + QualType destPointee + = S.Context.getQualifiedType(rhptee, lhptee.getQualifiers()); + QualType destType = S.Context.getPointerType(destPointee, destFPAttr); + // Add qualifiers or pointer attributes if necessary. + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_NoOp); + // Promote to void*. + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); + return destType; + } - // int or null -> A* - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null - return IntToPointer; - } + return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); +} - // In general, C pointers are not compatible with ObjC object pointers, - // with two exceptions: - if (isa(RHSType)) { - Kind = CK_CPointerToObjCPointerCast; +/// Return false if the first expression is not an integer and the second +/// expression is not a pointer, true otherwise. +static bool checkPointerIntegerMismatch(Sema &S, ExprResult &Int, + Expr* PointerExpr, SourceLocation Loc, + bool IsIntFirstExpr) { + if (!PointerExpr->getType()->isPointerType() || + !Int.get()->getType()->isIntegerType()) + return false; - // - conversions from 'void*' - if (RHSType->isVoidPointerType()) { - return Compatible; - } + Expr *Expr1 = IsIntFirstExpr ? Int.get() : PointerExpr; + Expr *Expr2 = IsIntFirstExpr ? PointerExpr : Int.get(); - // - conversions to 'Class' from its redefinition type - if (LHSType->isObjCClassType() && - Context.hasSameType(RHSType, - Context.getObjCClassRedefinitionType())) { - return Compatible; - } + S.Diag(Loc, diag::ext_typecheck_cond_pointer_integer_mismatch) + << Expr1->getType() << Expr2->getType() + << Expr1->getSourceRange() << Expr2->getSourceRange(); + Int = S.ImpCastExprToType(Int.get(), PointerExpr->getType(), + CK_IntegralToPointer); + return true; +} - return IncompatiblePointer; - } +/// Simple conversion between integer and floating point types. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// OpenCL v1.1 s6.3.i and s6.11.6 together require that the scalar +/// types are either integer or floating type. Between the two +/// operands, the type with the higher rank is defined as the "result +/// type". The other operand needs to be promoted to the same type. No +/// other type promotion is allowed. We cannot use +/// UsualArithmeticConversions() for this purpose, since it always +/// promotes promotable types. +static QualType OpenCLArithmeticConversions(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation QuestionLoc) { + LHS = S.DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - // Only under strict condition T^ is compatible with an Objective-C pointer. - if (RHSType->isBlockPointerType() && - LHSType->isBlockCompatibleObjCPointerType(Context)) { - if (ConvertRHS) - maybeExtendBlockObject(RHS); - Kind = CK_BlockPointerToObjCPointerCast; - return Compatible; - } + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = + S.Context.getCanonicalType(LHS.get()->getType()).getUnqualifiedType(); + QualType RHSType = + S.Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); - return Incompatible; + if (!LHSType->isIntegerType() && !LHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << LHSType << LHS.get()->getSourceRange(); + return QualType(); } - // Conversion to nullptr_t (C23 only) - if (getLangOpts().C23 && LHSType->isNullPtrType() && - RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) { - // null -> nullptr_t - Kind = CK_NullToPointer; - return Compatible; + if (!RHSType->isIntegerType() && !RHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << RHSType << RHS.get()->getSourceRange(); + return QualType(); } - // Conversions from pointers that are not covered by the above. - if (isa(RHSType)) { - // T* -> _Bool - if (LHSType == Context.BoolTy) { - Kind = CK_PointerToBoolean; - return Compatible; - } + // If both types are identical, no conversion is needed. + if (LHSType == RHSType) + return LHSType; - // T* -> int - if (LHSType->isIntegerType()) { - Kind = CK_PointerToIntegral; - return PointerToInt; - } + // Now handle "real" floating types (i.e. float, double, long double). + if (LHSType->isRealFloatingType() || RHSType->isRealFloatingType()) + return handleFloatConversion(S, LHS, RHS, LHSType, RHSType, + /*IsCompAssign = */ false); - return Incompatible; - } + // Finally, we have two differing integer types. + return handleIntegerConversion + (S, LHS, RHS, LHSType, RHSType, /*IsCompAssign = */ false); +} - // Conversions from Objective-C pointers that are not covered by the above. - if (isa(RHSType)) { - // T* -> _Bool - if (LHSType == Context.BoolTy) { - Kind = CK_PointerToBoolean; - return Compatible; - } +/// Convert scalar operands to a vector that matches the +/// condition in length. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// We first compute the "result type" for the scalar operands +/// according to OpenCL v1.1 s6.3.i. Both operands are then converted +/// into a vector of that type where the length matches the condition +/// vector type. s6.11.6 requires that the element types of the result +/// and the condition must have the same number of bits. +static QualType +OpenCLConvertScalarsToVectors(Sema &S, ExprResult &LHS, ExprResult &RHS, + QualType CondTy, SourceLocation QuestionLoc) { + QualType ResTy = OpenCLArithmeticConversions(S, LHS, RHS, QuestionLoc); + if (ResTy.isNull()) return QualType(); - // T* -> int - if (LHSType->isIntegerType()) { - Kind = CK_PointerToIntegral; - return PointerToInt; - } + const VectorType *CV = CondTy->getAs(); + assert(CV); - return Incompatible; - } + // Determine the vector result type + unsigned NumElements = CV->getNumElements(); + QualType VectorTy = S.Context.getExtVectorType(ResTy, NumElements); - // struct A -> struct B - if (isa(LHSType) && isa(RHSType)) { - if (Context.typesAreCompatible(LHSType, RHSType)) { - Kind = CK_NoOp; - return Compatible; - } + // Ensure that all types have the same number of bits + if (S.Context.getTypeSize(CV->getElementType()) + != S.Context.getTypeSize(ResTy)) { + // Since VectorTy is created internally, it does not pretty print + // with an OpenCL name. Instead, we just print a description. + std::string EleTyName = ResTy.getUnqualifiedType().getAsString(); + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << "(vector of " << NumElements << " '" << EleTyName << "' values)"; + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << OS.str(); + return QualType(); } - if (LHSType->isSamplerT() && RHSType->isIntegerType()) { - Kind = CK_IntToOCLSampler; - return Compatible; - } + // Convert operands to the vector result type + LHS = S.ImpCastExprToType(LHS.get(), VectorTy, CK_VectorSplat); + RHS = S.ImpCastExprToType(RHS.get(), VectorTy, CK_VectorSplat); - return Incompatible; + return VectorTy; } -/// Constructs a transparent union from an expression that is -/// used to initialize the transparent union. -static void ConstructTransparentUnion(Sema &S, ASTContext &C, - ExprResult &EResult, QualType UnionType, - FieldDecl *Field) { - // Build an initializer list that designates the appropriate member - // of the transparent union. - Expr *E = EResult.get(); - InitListExpr *Initializer = new (C) InitListExpr(C, SourceLocation(), - E, SourceLocation()); - Initializer->setType(UnionType); - Initializer->setInitializedFieldInUnion(Field); +/// Return false if this is a valid OpenCL condition vector +static bool checkOpenCLConditionVector(Sema &S, Expr *Cond, + SourceLocation QuestionLoc) { + // OpenCL v1.1 s6.11.6 says the elements of the vector must be of + // integral type. + const VectorType *CondTy = Cond->getType()->getAs(); + assert(CondTy); + QualType EleTy = CondTy->getElementType(); + if (EleTy->isIntegerType()) return false; - // Build a compound literal constructing a value of the transparent - // union type from this initializer list. - TypeSourceInfo *unionTInfo = C.getTrivialTypeSourceInfo(UnionType); - EResult = new (C) CompoundLiteralExpr(SourceLocation(), unionTInfo, UnionType, - VK_PRValue, Initializer, false); + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << Cond->getType() << Cond->getSourceRange(); + return true; } -Sema::AssignConvertType -Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType, - ExprResult &RHS) { - QualType RHSType = RHS.get()->getType(); - - // If the ArgType is a Union type, we want to handle a potential - // transparent_union GCC extension. - const RecordType *UT = ArgType->getAsUnionType(); - if (!UT || !UT->getDecl()->hasAttr()) - return Incompatible; +/// Return false if the vector condition type and the vector +/// result type are compatible. +/// +/// OpenCL v1.1 s6.11.6 requires that both vector types have the same +/// number of elements, and their element types have the same number +/// of bits. +static bool checkVectorResult(Sema &S, QualType CondTy, QualType VecResTy, + SourceLocation QuestionLoc) { + const VectorType *CV = CondTy->getAs(); + const VectorType *RV = VecResTy->getAs(); + assert(CV && RV); - // The field to initialize within the transparent union. - RecordDecl *UD = UT->getDecl(); - FieldDecl *InitField = nullptr; - // It's compatible if the expression matches any of the fields. - for (auto *it : UD->fields()) { - if (it->getType()->isPointerType()) { - // If the transparent union contains a pointer type, we allow: - // 1) void pointer - // 2) null pointer constant - if (RHSType->isPointerType()) - if (RHSType->castAs()->getPointeeType()->isVoidType()) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), CK_BitCast); - InitField = it; - break; - } + if (CV->getNumElements() != RV->getNumElements()) { + S.Diag(QuestionLoc, diag::err_conditional_vector_size) + << CondTy << VecResTy; + return true; + } - if (RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), - CK_NullToPointer); - InitField = it; - break; - } - } + QualType CVE = CV->getElementType(); + QualType RVE = RV->getElementType(); - CastKind Kind; - if (CheckAssignmentConstraints(it->getType(), RHS, Kind) - == Compatible) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), Kind); - InitField = it; - break; - } + if (S.Context.getTypeSize(CVE) != S.Context.getTypeSize(RVE)) { + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << VecResTy; + return true; } - if (!InitField) - return Incompatible; - - ConstructTransparentUnion(*this, Context, RHS, ArgType, InitField); - return Compatible; + return false; } -Sema::AssignConvertType -Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, - bool Diagnose, - bool DiagnoseCFAudited, - bool ConvertRHS) { - // We need to be able to tell the caller whether we diagnosed a problem, if - // they ask us to issue diagnostics. - assert((ConvertRHS || !Diagnose) && "can't indicate whether we diagnosed"); +/// Return the resulting type for the conditional operator in +/// OpenCL (aka "ternary selection operator", OpenCL v1.1 +/// s6.3.i) when the condition is a vector type. +static QualType +OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond, + ExprResult &LHS, ExprResult &RHS, + SourceLocation QuestionLoc) { + Cond = S.DefaultFunctionArrayLvalueConversion(Cond.get()); + if (Cond.isInvalid()) + return QualType(); + QualType CondTy = Cond.get()->getType(); - // If ConvertRHS is false, we want to leave the caller's RHS untouched. Sadly, - // we can't avoid *all* modifications at the moment, so we need some somewhere - // to put the updated value. - ExprResult LocalRHS = CallerRHS; - ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; + if (checkOpenCLConditionVector(S, Cond.get(), QuestionLoc)) + return QualType(); - if (const auto *LHSPtrType = LHSType->getAs()) { - if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { - if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && - !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { - Diag(RHS.get()->getExprLoc(), - diag::warn_noderef_to_dereferenceable_pointer) - << RHS.get()->getSourceRange(); - } - } + // If either operand is a vector then find the vector type of the + // result as specified in OpenCL v1.1 s6.3.i. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + bool IsBoolVecLang = + !S.getLangOpts().OpenCL && !S.getLangOpts().OpenCLCPlusPlus; + QualType VecResTy = + S.CheckVectorOperands(LHS, RHS, QuestionLoc, + /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ IsBoolVecLang, + /*ReportInvalid*/ true); + if (VecResTy.isNull()) + return QualType(); + // The result type must match the condition type as specified in + // OpenCL v1.1 s6.11.6. + if (checkVectorResult(S, CondTy, VecResTy, QuestionLoc)) + return QualType(); + return VecResTy; } - if (getLangOpts().CPlusPlus) { - if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { - // C++ 5.17p3: If the left operand is not of class type, the - // expression is implicitly converted (C++ 4) to the - // cv-unqualified type of the left operand. - QualType RHSType = RHS.get()->getType(); - if (Diagnose) { - RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - AA_Assigning); - } else { - ImplicitConversionSequence ICS = - TryImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - /*SuppressUserConversions=*/false, - AllowedExplicit::None, - /*InOverloadResolution=*/false, - /*CStyle=*/false, - /*AllowObjCWritebackConversion=*/false); - if (ICS.isFailure()) - return Incompatible; - RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - ICS, AA_Assigning); - } - if (RHS.isInvalid()) - return Incompatible; - Sema::AssignConvertType result = Compatible; - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - !ObjC().CheckObjCARCUnavailableWeakConversion(LHSType, RHSType)) - result = IncompatibleObjCWeakRef; - return result; - } + // Both operands are scalar. + return OpenCLConvertScalarsToVectors(S, LHS, RHS, CondTy, QuestionLoc); +} - // FIXME: Currently, we fall through and treat C++ classes like C - // structures. - // FIXME: We also fall through for atomics; not sure what should - // happen there, though. - } else if (RHS.get()->getType() == Context.OverloadTy) { - // As a set of extensions to C, we support overloading on functions. These - // functions need to be resolved here. - DeclAccessPair DAP; - if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction( - RHS.get(), LHSType, /*Complain=*/false, DAP)) - RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD); - else - return Incompatible; - } - - // This check seems unnatural, however it is necessary to ensure the proper - // conversion of functions/arrays. If the conversion were done for all - // DeclExpr's (created by ActOnIdExpression), it would mess up the unary - // expressions that suppress this implicit conversion (&, sizeof). This needs - // to happen before we check for null pointer conversions because C does not - // undergo the same implicit conversions as C++ does above (by the calls to - // TryImplicitConversion() and PerformImplicitConversion()) which insert the - // lvalue to rvalue cast before checking for null pointer constraints. This - // addresses code like: nullptr_t val; int *ptr; ptr = val; - // - // Suppress this for references: C++ 8.5.3p5. - if (!LHSType->isReferenceType()) { - // FIXME: We potentially allocate here even if ConvertRHS is false. - RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); - if (RHS.isInvalid()) - return Incompatible; - } - - // The constraints are expressed in terms of the atomic, qualified, or - // unqualified type of the LHS. - QualType LHSTypeAfterConversion = LHSType.getAtomicUnqualifiedType(); - - // C99 6.5.16.1p1: the left operand is a pointer and the right is - // a null pointer constant or its type is nullptr_t;. - if ((LHSTypeAfterConversion->isPointerType() || - LHSTypeAfterConversion->isObjCObjectPointerType() || - LHSTypeAfterConversion->isBlockPointerType()) && - ((getLangOpts().C23 && RHS.get()->getType()->isNullPtrType()) || - RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull))) { - if (Diagnose || ConvertRHS) { - CastKind Kind; - CXXCastPath Path; - CheckPointerConversion(RHS.get(), LHSType, Kind, Path, - /*IgnoreBaseAccess=*/false, Diagnose); - if (ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_PRValue, &Path); - } - return Compatible; - } - // C23 6.5.16.1p1: the left operand has type atomic, qualified, or - // unqualified bool, and the right operand is a pointer or its type is - // nullptr_t. - if (getLangOpts().C23 && LHSType->isBooleanType() && - RHS.get()->getType()->isNullPtrType()) { - // NB: T* -> _Bool is handled in CheckAssignmentConstraints, this only - // only handles nullptr -> _Bool due to needing an extra conversion - // step. - // We model this by converting from nullptr -> void * and then let the - // conversion from void * -> _Bool happen naturally. - if (Diagnose || ConvertRHS) { - CastKind Kind; - CXXCastPath Path; - CheckPointerConversion(RHS.get(), Context.VoidPtrTy, Kind, Path, - /*IgnoreBaseAccess=*/false, Diagnose); - if (ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), Context.VoidPtrTy, Kind, VK_PRValue, - &Path); +/// Return true if the Expr is block type +static bool checkBlockType(Sema &S, const Expr *E) { + if (const CallExpr *CE = dyn_cast(E)) { + QualType Ty = CE->getCallee()->getType(); + if (Ty->isBlockPointerType()) { + S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); + return true; } } + return false; +} - // OpenCL queue_t type assignment. - if (LHSType->isQueueT() && RHS.get()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNull)) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return Compatible; - } +/// Note that LHS is not null here, even if this is the gnu "x ?: y" extension. +/// In that case, LHS = cond. +/// C99 6.5.15 +QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, + ExprResult &RHS, ExprValueKind &VK, + ExprObjectKind &OK, + SourceLocation QuestionLoc) { - Expr *PRE = RHS.get()->IgnoreParenCasts(); - if (Diagnose && isa(PRE)) { - ObjCProtocolDecl *PDecl = cast(PRE)->getProtocol(); - if (PDecl && !PDecl->hasDefinition()) { - Diag(PRE->getExprLoc(), diag::warn_atprotocol_protocol) << PDecl; - Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; - } - } + ExprResult LHSResult = CheckPlaceholderExpr(LHS.get()); + if (!LHSResult.isUsable()) return QualType(); + LHS = LHSResult; - CastKind Kind; - Sema::AssignConvertType result = - CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); + ExprResult RHSResult = CheckPlaceholderExpr(RHS.get()); + if (!RHSResult.isUsable()) return QualType(); + RHS = RHSResult; - // C99 6.5.16.1p2: The value of the right operand is converted to the - // type of the assignment expression. - // CheckAssignmentConstraints allows the left-hand side to be a reference, - // so that we can use references in built-in functions even in C. - // The getNonReferenceType() call makes sure that the resulting expression - // does not have reference type. - if (result != Incompatible && RHS.get()->getType() != LHSType) { - QualType Ty = LHSType.getNonLValueExprType(Context); - Expr *E = RHS.get(); + // C++ is sufficiently different to merit its own checker. + if (getLangOpts().CPlusPlus) + return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc); - // Check for various Objective-C errors. If we are not reporting - // diagnostics and just checking for errors, e.g., during overload - // resolution, return Incompatible to indicate the failure. - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - ObjC().CheckObjCConversion(SourceRange(), Ty, E, - CheckedConversionKind::Implicit, Diagnose, - DiagnoseCFAudited) != SemaObjC::ACR_okay) { - if (!Diagnose) - return Incompatible; - } - if (getLangOpts().ObjC && - (ObjC().CheckObjCBridgeRelatedConversions(E->getBeginLoc(), LHSType, - E->getType(), E, Diagnose) || - ObjC().CheckConversionToObjCLiteral(LHSType, E, Diagnose))) { - if (!Diagnose) - return Incompatible; - // Replace the expression with a corrected version and continue so we - // can find further errors. - RHS = E; - return Compatible; - } + VK = VK_PRValue; + OK = OK_Ordinary; - if (ConvertRHS) - RHS = ImpCastExprToType(E, Ty, Kind); + if (Context.isDependenceAllowed() && + (Cond.get()->isTypeDependent() || LHS.get()->isTypeDependent() || + RHS.get()->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((Cond.get()->containsErrors() || LHS.get()->containsErrors() || + RHS.get()->containsErrors()) && + "should only occur in error-recovery path."); + return Context.DependentTy; } - return result; -} - -namespace { -/// The original operand to an operator, prior to the application of the usual -/// arithmetic conversions and converting the arguments of a builtin operator -/// candidate. -struct OriginalOperand { - explicit OriginalOperand(Expr *Op) : Orig(Op), Conversion(nullptr) { - if (auto *MTE = dyn_cast(Op)) - Op = MTE->getSubExpr(); - if (auto *BTE = dyn_cast(Op)) - Op = BTE->getSubExpr(); - if (auto *ICE = dyn_cast(Op)) { - Orig = ICE->getSubExprAsWritten(); - Conversion = ICE->getConversionFunction(); - } - } + // The OpenCL operator with a vector condition is sufficiently + // different to merit its own checker. + if ((getLangOpts().OpenCL && Cond.get()->getType()->isVectorType()) || + Cond.get()->getType()->isExtVectorType()) + return OpenCLCheckVectorConditional(*this, Cond, LHS, RHS, QuestionLoc); - QualType getType() const { return Orig->getType(); } + // First, check the condition. + Cond = UsualUnaryConversions(Cond.get()); + if (Cond.isInvalid()) + return QualType(); + if (checkCondition(*this, Cond.get(), QuestionLoc)) + return QualType(); - Expr *Orig; - NamedDecl *Conversion; -}; -} + // Handle vectors. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); -QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &LHS, - ExprResult &RHS) { - OriginalOperand OrigLHS(LHS.get()), OrigRHS(RHS.get()); + QualType ResTy = + UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); - Diag(Loc, diag::err_typecheck_invalid_operands) - << OrigLHS.getType() << OrigRHS.getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + // WebAssembly tables are not allowed as conditional LHS or RHS. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - // If a user-defined conversion was applied to either of the operands prior - // to applying the built-in operator rules, tell the user about it. - if (OrigLHS.Conversion) { - Diag(OrigLHS.Conversion->getLocation(), - diag::note_typecheck_invalid_operands_converted) - << 0 << LHS.get()->getType(); + // Diagnose attempts to convert between __ibm128, __float128 and long double + // where such conversions currently can't be handled. + if (unsupportedTypeConversion(*this, LHSTy, RHSTy)) { + Diag(QuestionLoc, + diag::err_typecheck_cond_incompatible_operands) << LHSTy << RHSTy + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } - if (OrigRHS.Conversion) { - Diag(OrigRHS.Conversion->getLocation(), - diag::note_typecheck_invalid_operands_converted) - << 1 << RHS.get()->getType(); + + // OpenCL v2.0 s6.12.5 - Blocks cannot be used as expressions of the ternary + // selection operator (?:). + if (getLangOpts().OpenCL && + ((int)checkBlockType(*this, LHS.get()) | (int)checkBlockType(*this, RHS.get()))) { + return QualType(); } - return QualType(); -} + // If both operands have arithmetic type, do the usual arithmetic conversions + // to find a common type: C99 6.5.15p3,5. + if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { + // Disallow invalid arithmetic conversions, such as those between bit- + // precise integers types of different sizes, or between a bit-precise + // integer and another type. + if (ResTy.isNull() && (LHSTy->isBitIntType() || RHSTy->isBitIntType())) { + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } -QualType Sema::InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS, - ExprResult &RHS) { - QualType LHSType = LHS.get()->IgnoreImpCasts()->getType(); - QualType RHSType = RHS.get()->IgnoreImpCasts()->getType(); + LHS = ImpCastExprToType(LHS.get(), ResTy, PrepareScalarCast(LHS, ResTy)); + RHS = ImpCastExprToType(RHS.get(), ResTy, PrepareScalarCast(RHS, ResTy)); - bool LHSNatVec = LHSType->isVectorType(); - bool RHSNatVec = RHSType->isVectorType(); + return ResTy; + } - if (!(LHSNatVec && RHSNatVec)) { - Expr *Vector = LHSNatVec ? LHS.get() : RHS.get(); - Expr *NonVector = !LHSNatVec ? LHS.get() : RHS.get(); - Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) - << 0 << Vector->getType() << NonVector->IgnoreImpCasts()->getType() - << Vector->getSourceRange(); - return QualType(); + // If both operands are the same structure or union type, the result is that + // type. + if (const RecordType *LHSRT = LHSTy->getAs()) { // C99 6.5.15p3 + if (const RecordType *RHSRT = RHSTy->getAs()) + if (LHSRT->getDecl() == RHSRT->getDecl()) + // "If both the operands have structure or union type, the result has + // that type." This implies that CV qualifiers are dropped. + return Context.getCommonSugaredType(LHSTy.getUnqualifiedType(), + RHSTy.getUnqualifiedType()); + // FIXME: Type of conditional expression must be complete in C mode. } - Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) - << 1 << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - - return QualType(); -} - -/// Try to convert a value of non-vector type to a vector type by converting -/// the type to the element type of the vector and then performing a splat. -/// If the language is OpenCL, we only use conversions that promote scalar -/// rank; for C, Obj-C, and C++ we allow any real scalar conversion except -/// for float->int. -/// -/// OpenCL V2.0 6.2.6.p2: -/// An error shall occur if any scalar operand type has greater rank -/// than the type of the vector element. -/// -/// \param scalar - if non-null, actually perform the conversions -/// \return true if the operation fails (but without diagnosing the failure) -static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar, - QualType scalarTy, - QualType vectorEltTy, - QualType vectorTy, - unsigned &DiagID) { - // The conversion to apply to the scalar before splatting it, - // if necessary. - CastKind scalarCast = CK_NoOp; - - if (vectorEltTy->isIntegralType(S.Context)) { - if (S.getLangOpts().OpenCL && (scalarTy->isRealFloatingType() || - (scalarTy->isIntegerType() && - S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0))) { - DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; - return true; - } - if (!scalarTy->isIntegralType(S.Context)) - return true; - scalarCast = CK_IntegralCast; - } else if (vectorEltTy->isRealFloatingType()) { - if (scalarTy->isRealFloatingType()) { - if (S.getLangOpts().OpenCL && - S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) { - DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; - return true; - } - scalarCast = CK_FloatingCast; + // C99 6.5.15p5: "If both operands have void type, the result has void type." + // The following || allows only one side to be void (a GCC-ism). + if (LHSTy->isVoidType() || RHSTy->isVoidType()) { + QualType ResTy; + if (LHSTy->isVoidType() && RHSTy->isVoidType()) { + ResTy = Context.getCommonSugaredType(LHSTy, RHSTy); + } else if (RHSTy->isVoidType()) { + ResTy = RHSTy; + Diag(RHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) + << RHS.get()->getSourceRange(); + } else { + ResTy = LHSTy; + Diag(LHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) + << LHS.get()->getSourceRange(); } - else if (scalarTy->isIntegralType(S.Context)) - scalarCast = CK_IntegralToFloating; - else - return true; - } else { - return true; - } - - // Adjust scalar if desired. - if (scalar) { - if (scalarCast != CK_NoOp) - *scalar = S.ImpCastExprToType(scalar->get(), vectorEltTy, scalarCast); - *scalar = S.ImpCastExprToType(scalar->get(), vectorTy, CK_VectorSplat); + LHS = ImpCastExprToType(LHS.get(), ResTy, CK_ToVoid); + RHS = ImpCastExprToType(RHS.get(), ResTy, CK_ToVoid); + return ResTy; } - return false; -} -/// Convert vector E to a vector with the same number of elements but different -/// element type. -static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) { - const auto *VecTy = E->getType()->getAs(); - assert(VecTy && "Expression E must be a vector"); - QualType NewVecTy = - VecTy->isExtVectorType() - ? S.Context.getExtVectorType(ElementType, VecTy->getNumElements()) - : S.Context.getVectorType(ElementType, VecTy->getNumElements(), - VecTy->getVectorKind()); + // C23 6.5.15p7: + // ... if both the second and third operands have nullptr_t type, the + // result also has that type. + if (LHSTy->isNullPtrType() && Context.hasSameType(LHSTy, RHSTy)) + return ResTy; - // Look through the implicit cast. Return the subexpression if its type is - // NewVecTy. - if (auto *ICE = dyn_cast(E)) - if (ICE->getSubExpr()->getType() == NewVecTy) - return ICE->getSubExpr(); + // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has + // the type of the other operand." + if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; + if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; - auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast; - return S.ImpCastExprToType(E, NewVecTy, Cast); -} + // All objective-c pointer type analysis is done here. + QualType compositeType = + ObjC().FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (!compositeType.isNull()) + return compositeType; -/// Test if a (constant) integer Int can be casted to another integer type -/// IntTy without losing precision. -static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int, - QualType OtherIntTy) { - QualType IntTy = Int->get()->getType().getUnqualifiedType(); - // Reject cases where the value of the Int is unknown as that would - // possibly cause truncation, but accept cases where the scalar can be - // demoted without loss of precision. - Expr::EvalResult EVResult; - bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); - int Order = S.Context.getIntegerTypeOrder(OtherIntTy, IntTy); - bool IntSigned = IntTy->hasSignedIntegerRepresentation(); - bool OtherIntSigned = OtherIntTy->hasSignedIntegerRepresentation(); + // Handle block pointer types. + if (LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) + return checkConditionalBlockPointerCompatibility(*this, LHS, RHS, + QuestionLoc); - if (CstInt) { - // If the scalar is constant and is of a higher order and has more active - // bits that the vector element type, reject it. - llvm::APSInt Result = EVResult.Val.getInt(); - unsigned NumBits = IntSigned - ? (Result.isNegative() ? Result.getSignificantBits() - : Result.getActiveBits()) - : Result.getActiveBits(); - if (Order < 0 && S.Context.getIntWidth(OtherIntTy) < NumBits) - return true; + // Check constraints for C object pointers types (C99 6.5.15p3,6). + if (LHSTy->isPointerType() && RHSTy->isPointerType()) + return checkConditionalObjectPointersCompatibility(*this, LHS, RHS, + QuestionLoc); - // If the signedness of the scalar type and the vector element type - // differs and the number of bits is greater than that of the vector - // element reject it. - return (IntSigned != OtherIntSigned && - NumBits > S.Context.getIntWidth(OtherIntTy)); - } + // GCC compatibility: soften pointer/integer mismatch. Note that + // null pointers have been filtered out by this point. + if (checkPointerIntegerMismatch(*this, LHS, RHS.get(), QuestionLoc, + /*IsIntFirstExpr=*/true)) + return RHSTy; + if (checkPointerIntegerMismatch(*this, RHS, LHS.get(), QuestionLoc, + /*IsIntFirstExpr=*/false)) + return LHSTy; - // Reject cases where the value of the scalar is not constant and it's - // order is greater than that of the vector element type. - return (Order < 0); -} + // Emit a better diagnostic if one of the expressions is a null pointer + // constant and the other is not a pointer type. In this case, the user most + // likely forgot to take the address of the other expression. + if (DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc)) + return QualType(); -/// Test if a (constant) integer Int can be casted to floating point type -/// FloatTy without losing precision. -static bool canConvertIntTyToFloatTy(Sema &S, ExprResult *Int, - QualType FloatTy) { - QualType IntTy = Int->get()->getType().getUnqualifiedType(); + // Finally, if the LHS and RHS types are canonically the same type, we can + // use the common sugared type. + if (Context.hasSameType(LHSTy, RHSTy)) + return Context.getCommonSugaredType(LHSTy, RHSTy); - // Determine if the integer constant can be expressed as a floating point - // number of the appropriate type. - Expr::EvalResult EVResult; - bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); + // Otherwise, the operands are not compatible. + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); +} - uint64_t Bits = 0; - if (CstInt) { - // Reject constants that would be truncated if they were converted to - // the floating point type. Test by simple to/from conversion. - // FIXME: Ideally the conversion to an APFloat and from an APFloat - // could be avoided if there was a convertFromAPInt method - // which could signal back if implicit truncation occurred. - llvm::APSInt Result = EVResult.Val.getInt(); - llvm::APFloat Float(S.Context.getFloatTypeSemantics(FloatTy)); - Float.convertFromAPInt(Result, IntTy->hasSignedIntegerRepresentation(), - llvm::APFloat::rmTowardZero); - llvm::APSInt ConvertBack(S.Context.getIntWidth(IntTy), - !IntTy->hasSignedIntegerRepresentation()); - bool Ignored = false; - Float.convertToInteger(ConvertBack, llvm::APFloat::rmNearestTiesToEven, - &Ignored); - if (Result != ConvertBack) - return true; +/// SuggestParentheses - Emit a note with a fixit hint that wraps +/// ParenRange in parentheses. +static void SuggestParentheses(Sema &Self, SourceLocation Loc, + const PartialDiagnostic &Note, + SourceRange ParenRange) { + SourceLocation EndLoc = Self.getLocForEndOfToken(ParenRange.getEnd()); + if (ParenRange.getBegin().isFileID() && ParenRange.getEnd().isFileID() && + EndLoc.isValid()) { + Self.Diag(Loc, Note) + << FixItHint::CreateInsertion(ParenRange.getBegin(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); } else { - // Reject types that cannot be fully encoded into the mantissa of - // the float. - Bits = S.Context.getTypeSize(IntTy); - unsigned FloatPrec = llvm::APFloat::semanticsPrecision( - S.Context.getFloatTypeSemantics(FloatTy)); - if (Bits > FloatPrec) - return true; + // We can't display the parentheses, so just show the bare note. + Self.Diag(Loc, Note) << ParenRange; } +} - return false; +static bool IsArithmeticOp(BinaryOperatorKind Opc) { + return BinaryOperator::isAdditiveOp(Opc) || + BinaryOperator::isMultiplicativeOp(Opc) || + BinaryOperator::isShiftOp(Opc) || Opc == BO_And || Opc == BO_Or; + // This only checks for bitwise-or and bitwise-and, but not bitwise-xor and + // not any of the logical operators. Bitwise-xor is commonly used as a + // logical-xor because there is no logical-xor operator. The logical + // operators, including uses of xor, have a high false positive rate for + // precedence warnings. } -/// Attempt to convert and splat Scalar into a vector whose types matches -/// Vector following GCC conversion rules. The rule is that implicit -/// conversion can occur when Scalar can be casted to match Vector's element -/// type without causing truncation of Scalar. -static bool tryGCCVectorConvertAndSplat(Sema &S, ExprResult *Scalar, - ExprResult *Vector) { - QualType ScalarTy = Scalar->get()->getType().getUnqualifiedType(); - QualType VectorTy = Vector->get()->getType().getUnqualifiedType(); - QualType VectorEltTy; - - if (const auto *VT = VectorTy->getAs()) { - assert(!isa(VT) && - "ExtVectorTypes should not be handled here!"); - VectorEltTy = VT->getElementType(); - } else if (VectorTy->isSveVLSBuiltinType()) { - VectorEltTy = - VectorTy->castAs()->getSveEltType(S.getASTContext()); - } else { - llvm_unreachable("Only Fixed-Length and SVE Vector types are handled here"); +/// IsArithmeticBinaryExpr - Returns true if E is an arithmetic binary +/// expression, either using a built-in or overloaded operator, +/// and sets *OpCode to the opcode and *RHSExprs to the right-hand side +/// expression. +static bool IsArithmeticBinaryExpr(const Expr *E, BinaryOperatorKind *Opcode, + const Expr **RHSExprs) { + // Don't strip parenthesis: we should not warn if E is in parenthesis. + E = E->IgnoreImpCasts(); + E = E->IgnoreConversionOperatorSingleStep(); + E = E->IgnoreImpCasts(); + if (const auto *MTE = dyn_cast(E)) { + E = MTE->getSubExpr(); + E = E->IgnoreImpCasts(); } - // Reject cases where the vector element type or the scalar element type are - // not integral or floating point types. - if (!VectorEltTy->isArithmeticType() || !ScalarTy->isArithmeticType()) + // Built-in binary operator. + if (const auto *OP = dyn_cast(E); + OP && IsArithmeticOp(OP->getOpcode())) { + *Opcode = OP->getOpcode(); + *RHSExprs = OP->getRHS(); return true; + } - // The conversion to apply to the scalar before splatting it, - // if necessary. - CastKind ScalarCast = CK_NoOp; - - // Accept cases where the vector elements are integers and the scalar is - // an integer. - // FIXME: Notionally if the scalar was a floating point value with a precise - // integral representation, we could cast it to an appropriate integer - // type and then perform the rest of the checks here. GCC will perform - // this conversion in some cases as determined by the input language. - // We should accept it on a language independent basis. - if (VectorEltTy->isIntegralType(S.Context) && - ScalarTy->isIntegralType(S.Context) && - S.Context.getIntegerTypeOrder(VectorEltTy, ScalarTy)) { + // Overloaded operator. + if (const auto *Call = dyn_cast(E)) { + if (Call->getNumArgs() != 2) + return false; - if (canConvertIntToOtherIntTy(S, Scalar, VectorEltTy)) - return true; + // Make sure this is really a binary operator that is safe to pass into + // BinaryOperator::getOverloadedOpcode(), e.g. it's not a subscript op. + OverloadedOperatorKind OO = Call->getOperator(); + if (OO < OO_Plus || OO > OO_Arrow || + OO == OO_PlusPlus || OO == OO_MinusMinus) + return false; - ScalarCast = CK_IntegralCast; - } else if (VectorEltTy->isIntegralType(S.Context) && - ScalarTy->isRealFloatingType()) { - if (S.Context.getTypeSize(VectorEltTy) == S.Context.getTypeSize(ScalarTy)) - ScalarCast = CK_FloatingToIntegral; - else + BinaryOperatorKind OpKind = BinaryOperator::getOverloadedOpcode(OO); + if (IsArithmeticOp(OpKind)) { + *Opcode = OpKind; + *RHSExprs = Call->getArg(1); return true; - } else if (VectorEltTy->isRealFloatingType()) { - if (ScalarTy->isRealFloatingType()) { - - // Reject cases where the scalar type is not a constant and has a higher - // Order than the vector element type. - llvm::APFloat Result(0.0); - - // Determine whether this is a constant scalar. In the event that the - // value is dependent (and thus cannot be evaluated by the constant - // evaluator), skip the evaluation. This will then diagnose once the - // expression is instantiated. - bool CstScalar = Scalar->get()->isValueDependent() || - Scalar->get()->EvaluateAsFloat(Result, S.Context); - int Order = S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy); - if (!CstScalar && Order < 0) - return true; + } + } - // If the scalar cannot be safely casted to the vector element type, - // reject it. - if (CstScalar) { - bool Truncated = false; - Result.convert(S.Context.getFloatTypeSemantics(VectorEltTy), - llvm::APFloat::rmNearestTiesToEven, &Truncated); - if (Truncated) - return true; - } + return false; +} - ScalarCast = CK_FloatingCast; - } else if (ScalarTy->isIntegralType(S.Context)) { - if (canConvertIntTyToFloatTy(S, Scalar, VectorEltTy)) - return true; +/// ExprLooksBoolean - Returns true if E looks boolean, i.e. it has boolean type +/// or is a logical expression such as (x==y) which has int type, but is +/// commonly interpreted as boolean. +static bool ExprLooksBoolean(const Expr *E) { + E = E->IgnoreParenImpCasts(); - ScalarCast = CK_IntegralToFloating; - } else - return true; - } else if (ScalarTy->isEnumeralType()) + if (E->getType()->isBooleanType()) + return true; + if (const auto *OP = dyn_cast(E)) + return OP->isComparisonOp() || OP->isLogicalOp(); + if (const auto *OP = dyn_cast(E)) + return OP->getOpcode() == UO_LNot; + if (E->getType()->isPointerType()) return true; + // FIXME: What about overloaded operator calls returning "unspecified boolean + // type"s (commonly pointer-to-members)? - // Adjust scalar if desired. - if (ScalarCast != CK_NoOp) - *Scalar = S.ImpCastExprToType(Scalar->get(), VectorEltTy, ScalarCast); - *Scalar = S.ImpCastExprToType(Scalar->get(), VectorTy, CK_VectorSplat); return false; } -QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompAssign, - bool AllowBothBool, - bool AllowBoolConversions, - bool AllowBoolOperation, - bool ReportInvalid) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); +/// DiagnoseConditionalPrecedence - Emit a warning when a conditional operator +/// and binary operator are mixed in a way that suggests the programmer assumed +/// the conditional operator has higher precedence, for example: +/// "int x = a + someBinaryCondition ? 1 : 2". +static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc, + Expr *Condition, const Expr *LHSExpr, + const Expr *RHSExpr) { + BinaryOperatorKind CondOpcode; + const Expr *CondRHS; - const VectorType *LHSVecType = LHSType->getAs(); - const VectorType *RHSVecType = RHSType->getAs(); - assert(LHSVecType || RHSVecType); + if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS)) + return; + if (!ExprLooksBoolean(CondRHS)) + return; - // AltiVec-style "vector bool op vector bool" combinations are allowed - // for some operators but not others. - if (!AllowBothBool && LHSVecType && - LHSVecType->getVectorKind() == VectorKind::AltiVecBool && RHSVecType && - RHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); + // The condition is an arithmetic binary expression, with a right- + // hand side that looks boolean, so warn. - // This operation may not be performed on boolean vectors. - if (!AllowBoolOperation && - (LHSType->isExtVectorBoolType() || RHSType->isExtVectorBoolType())) - return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); + unsigned DiagID = BinaryOperator::isBitwiseOp(CondOpcode) + ? diag::warn_precedence_bitwise_conditional + : diag::warn_precedence_conditional; - // If the vector types are identical, return. - if (Context.hasSameType(LHSType, RHSType)) - return Context.getCommonSugaredType(LHSType, RHSType); + Self.Diag(OpLoc, DiagID) + << Condition->getSourceRange() + << BinaryOperator::getOpcodeStr(CondOpcode); - // If we have compatible AltiVec and GCC vector types, use the AltiVec type. - if (LHSVecType && RHSVecType && - Context.areCompatibleVectorTypes(LHSType, RHSType)) { - if (isa(LHSVecType)) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return LHSType; - } + SuggestParentheses( + Self, OpLoc, + Self.PDiag(diag::note_precedence_silence) + << BinaryOperator::getOpcodeStr(CondOpcode), + SourceRange(Condition->getBeginLoc(), Condition->getEndLoc())); - if (!IsCompAssign) - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - return RHSType; - } + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_conditional_first), + SourceRange(CondRHS->getBeginLoc(), RHSExpr->getEndLoc())); +} - // AllowBoolConversions says that bool and non-bool AltiVec vectors - // can be mixed, with the result being the non-bool type. The non-bool - // operand must have integer element type. - if (AllowBoolConversions && LHSVecType && RHSVecType && - LHSVecType->getNumElements() == RHSVecType->getNumElements() && - (Context.getTypeSize(LHSVecType->getElementType()) == - Context.getTypeSize(RHSVecType->getElementType()))) { - if (LHSVecType->getVectorKind() == VectorKind::AltiVecVector && - LHSVecType->getElementType()->isIntegerType() && - RHSVecType->getVectorKind() == VectorKind::AltiVecBool) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return LHSType; - } - if (!IsCompAssign && - LHSVecType->getVectorKind() == VectorKind::AltiVecBool && - RHSVecType->getVectorKind() == VectorKind::AltiVecVector && - RHSVecType->getElementType()->isIntegerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - return RHSType; - } - } +/// Compute the nullability of a conditional expression. +static QualType computeConditionalNullability(QualType ResTy, bool IsBin, + QualType LHSTy, QualType RHSTy, + ASTContext &Ctx) { + if (!ResTy->isAnyPointerType()) + return ResTy; - // Expressions containing fixed-length and sizeless SVE/RVV vectors are - // invalid since the ambiguity can affect the ABI. - auto IsSveRVVConversion = [](QualType FirstType, QualType SecondType, - unsigned &SVEorRVV) { - const VectorType *VecType = SecondType->getAs(); - SVEorRVV = 0; - if (FirstType->isSizelessBuiltinType() && VecType) { - if (VecType->getVectorKind() == VectorKind::SveFixedLengthData || - VecType->getVectorKind() == VectorKind::SveFixedLengthPredicate) - return true; - if (VecType->getVectorKind() == VectorKind::RVVFixedLengthData || - VecType->getVectorKind() == VectorKind::RVVFixedLengthMask) { - SVEorRVV = 1; - return true; - } + auto GetNullability = [](QualType Ty) { + std::optional Kind = Ty->getNullability(); + if (Kind) { + // For our purposes, treat _Nullable_result as _Nullable. + if (*Kind == NullabilityKind::NullableResult) + return NullabilityKind::Nullable; + return *Kind; } - - return false; + return NullabilityKind::Unspecified; }; - unsigned SVEorRVV; - if (IsSveRVVConversion(LHSType, RHSType, SVEorRVV) || - IsSveRVVConversion(RHSType, LHSType, SVEorRVV)) { - Diag(Loc, diag::err_typecheck_sve_rvv_ambiguous) - << SVEorRVV << LHSType << RHSType; - return QualType(); + auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); + NullabilityKind MergedKind; + + // Compute nullability of a binary conditional expression. + if (IsBin) { + if (LHSKind == NullabilityKind::NonNull) + MergedKind = NullabilityKind::NonNull; + else + MergedKind = RHSKind; + // Compute nullability of a normal conditional expression. + } else { + if (LHSKind == NullabilityKind::Nullable || + RHSKind == NullabilityKind::Nullable) + MergedKind = NullabilityKind::Nullable; + else if (LHSKind == NullabilityKind::NonNull) + MergedKind = RHSKind; + else if (RHSKind == NullabilityKind::NonNull) + MergedKind = LHSKind; + else + MergedKind = NullabilityKind::Unspecified; } - // Expressions containing GNU and SVE or RVV (fixed or sizeless) vectors are - // invalid since the ambiguity can affect the ABI. - auto IsSveRVVGnuConversion = [](QualType FirstType, QualType SecondType, - unsigned &SVEorRVV) { - const VectorType *FirstVecType = FirstType->getAs(); - const VectorType *SecondVecType = SecondType->getAs(); + // Return if ResTy already has the correct nullability. + if (GetNullability(ResTy) == MergedKind) + return ResTy; - SVEorRVV = 0; - if (FirstVecType && SecondVecType) { - if (FirstVecType->getVectorKind() == VectorKind::Generic) { - if (SecondVecType->getVectorKind() == VectorKind::SveFixedLengthData || - SecondVecType->getVectorKind() == - VectorKind::SveFixedLengthPredicate) - return true; - if (SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthData || - SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthMask) { - SVEorRVV = 1; - return true; - } - } - return false; - } + // Strip all nullability from ResTy. + while (ResTy->getNullability()) + ResTy = ResTy.getSingleStepDesugaredType(Ctx); - if (SecondVecType && - SecondVecType->getVectorKind() == VectorKind::Generic) { - if (FirstType->isSVESizelessBuiltinType()) - return true; - if (FirstType->isRVVSizelessBuiltinType()) { - SVEorRVV = 1; - return true; - } + // Create a new AttributedType with the new nullability kind. + return Ctx.getAttributedType(MergedKind, ResTy, ResTy); +} + +ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, + SourceLocation ColonLoc, + Expr *CondExpr, Expr *LHSExpr, + Expr *RHSExpr) { + if (!Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes in the condition because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + ExprResult CondResult = CorrectDelayedTyposInExpr(CondExpr); + ExprResult LHSResult = CorrectDelayedTyposInExpr(LHSExpr); + ExprResult RHSResult = CorrectDelayedTyposInExpr(RHSExpr); + + if (!CondResult.isUsable()) + return ExprError(); + + if (LHSExpr) { + if (!LHSResult.isUsable()) + return ExprError(); } - return false; - }; + if (!RHSResult.isUsable()) + return ExprError(); - if (IsSveRVVGnuConversion(LHSType, RHSType, SVEorRVV) || - IsSveRVVGnuConversion(RHSType, LHSType, SVEorRVV)) { - Diag(Loc, diag::err_typecheck_sve_rvv_gnu_ambiguous) - << SVEorRVV << LHSType << RHSType; - return QualType(); + CondExpr = CondResult.get(); + LHSExpr = LHSResult.get(); + RHSExpr = RHSResult.get(); } - // If there's a vector type and a scalar, try to convert the scalar to - // the vector element type and splat. - unsigned DiagID = diag::err_typecheck_vector_not_convertable; - if (!RHSVecType) { - if (isa(LHSVecType)) { - if (!tryVectorConvertAndSplat(*this, &RHS, RHSType, - LHSVecType->getElementType(), LHSType, - DiagID)) - return LHSType; - } else { - if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) - return LHSType; + // If this is the gnu "x ?: y" extension, analyze the types as though the LHS + // was the condition. + OpaqueValueExpr *opaqueValue = nullptr; + Expr *commonExpr = nullptr; + if (!LHSExpr) { + commonExpr = CondExpr; + // Lower out placeholder types first. This is important so that we don't + // try to capture a placeholder. This happens in few cases in C++; such + // as Objective-C++'s dictionary subscripting syntax. + if (commonExpr->hasPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(commonExpr); + if (!result.isUsable()) return ExprError(); + commonExpr = result.get(); } - } - if (!LHSVecType) { - if (isa(RHSVecType)) { - if (!tryVectorConvertAndSplat(*this, (IsCompAssign ? nullptr : &LHS), - LHSType, RHSVecType->getElementType(), - RHSType, DiagID)) - return RHSType; - } else { - if (LHS.get()->isLValue() || - !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) - return RHSType; + // We usually want to apply unary conversions *before* saving, except + // in the special case of a C++ l-value conditional. + if (!(getLangOpts().CPlusPlus + && !commonExpr->isTypeDependent() + && commonExpr->getValueKind() == RHSExpr->getValueKind() + && commonExpr->isGLValue() + && commonExpr->isOrdinaryOrBitFieldObject() + && RHSExpr->isOrdinaryOrBitFieldObject() + && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) { + ExprResult commonRes = UsualUnaryConversions(commonExpr); + if (commonRes.isInvalid()) + return ExprError(); + commonExpr = commonRes.get(); } - } - // FIXME: The code below also handles conversion between vectors and - // non-scalars, we should break this down into fine grained specific checks - // and emit proper diagnostics. - QualType VecType = LHSVecType ? LHSType : RHSType; - const VectorType *VT = LHSVecType ? LHSVecType : RHSVecType; - QualType OtherType = LHSVecType ? RHSType : LHSType; - ExprResult *OtherExpr = LHSVecType ? &RHS : &LHS; - if (isLaxVectorConversion(OtherType, VecType)) { - if (Context.getTargetInfo().getTriple().isPPC() && - anyAltivecTypes(RHSType, LHSType) && - !Context.areCompatibleVectorTypes(RHSType, LHSType)) - Diag(Loc, diag::warn_deprecated_lax_vec_conv_all) << RHSType << LHSType; - // If we're allowing lax vector conversions, only the total (data) size - // needs to be the same. For non compound assignment, if one of the types is - // scalar, the result is always the vector type. - if (!IsCompAssign) { - *OtherExpr = ImpCastExprToType(OtherExpr->get(), VecType, CK_BitCast); - return VecType; - // In a compound assignment, lhs += rhs, 'lhs' is a lvalue src, forbidding - // any implicit cast. Here, the 'rhs' should be implicit casted to 'lhs' - // type. Note that this is already done by non-compound assignments in - // CheckAssignmentConstraints. If it's a scalar type, only bitcast for - // <1 x T> -> T. The result is also a vector type. - } else if (OtherType->isExtVectorType() || OtherType->isVectorType() || - (OtherType->isScalarType() && VT->getNumElements() == 1)) { - ExprResult *RHSExpr = &RHS; - *RHSExpr = ImpCastExprToType(RHSExpr->get(), LHSType, CK_BitCast); - return VecType; + // If the common expression is a class or array prvalue, materialize it + // so that we can safely refer to it multiple times. + if (commonExpr->isPRValue() && (commonExpr->getType()->isRecordType() || + commonExpr->getType()->isArrayType())) { + ExprResult MatExpr = TemporaryMaterializationConversion(commonExpr); + if (MatExpr.isInvalid()) + return ExprError(); + commonExpr = MatExpr.get(); } - } - - // Okay, the expression is invalid. - // If there's a non-vector, non-real operand, diagnose that. - if ((!RHSVecType && !RHSType->isRealType()) || - (!LHSVecType && !LHSType->isRealType())) { - Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) - << LHSType << RHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); + opaqueValue = new (Context) OpaqueValueExpr(commonExpr->getExprLoc(), + commonExpr->getType(), + commonExpr->getValueKind(), + commonExpr->getObjectKind(), + commonExpr); + LHSExpr = CondExpr = opaqueValue; } - // OpenCL V1.1 6.2.6.p1: - // If the operands are of more than one vector type, then an error shall - // occur. Implicit conversions between vector types are not permitted, per - // section 6.2.1. - if (getLangOpts().OpenCL && - RHSVecType && isa(RHSVecType) && - LHSVecType && isa(LHSVecType)) { - Diag(Loc, diag::err_opencl_implicit_vector_conversion) << LHSType - << RHSType; - return QualType(); - } + QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr; + QualType result = CheckConditionalOperands(Cond, LHS, RHS, + VK, OK, QuestionLoc); + if (result.isNull() || Cond.isInvalid() || LHS.isInvalid() || + RHS.isInvalid()) + return ExprError(); + DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), + RHS.get()); - // If there is a vector type that is not a ExtVector and a scalar, we reach - // this point if scalar could not be converted to the vector's element type - // without truncation. - if ((RHSVecType && !isa(RHSVecType)) || - (LHSVecType && !isa(LHSVecType))) { - QualType Scalar = LHSVecType ? RHSType : LHSType; - QualType Vector = LHSVecType ? LHSType : RHSType; - unsigned ScalarOrVector = LHSVecType && RHSVecType ? 1 : 0; - Diag(Loc, - diag::err_typecheck_vector_not_convertable_implict_truncation) - << ScalarOrVector << Scalar << Vector; + CheckBoolLikeConversion(Cond.get(), QuestionLoc); - return QualType(); - } + result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, + Context); - // Otherwise, use the generic diagnostic. - Diag(Loc, DiagID) - << LHSType << RHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); + if (!commonExpr) + return new (Context) + ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, + RHS.get(), result, VK, OK); + + return new (Context) BinaryConditionalOperator( + commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, + ColonLoc, result, VK, OK); } -QualType Sema::CheckSizelessVectorOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign, - ArithConvKind OperationKind) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); +bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { + unsigned FromAttributes = 0, ToAttributes = 0; + if (const auto *FromFn = + dyn_cast(Context.getCanonicalType(FromType))) + FromAttributes = + FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + if (const auto *ToFn = + dyn_cast(Context.getCanonicalType(ToType))) + ToAttributes = + ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + return FromAttributes != ToAttributes; +} - const BuiltinType *LHSBuiltinTy = LHSType->getAs(); - const BuiltinType *RHSBuiltinTy = RHSType->getAs(); +// Check if we have a conversion between incompatible cmse function pointer +// types, that is, a conversion between a function pointer with the +// cmse_nonsecure_call attribute and one without. +static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType, + QualType ToType) { + if (const auto *ToFn = + dyn_cast(S.Context.getCanonicalType(ToType))) { + if (const auto *FromFn = + dyn_cast(S.Context.getCanonicalType(FromType))) { + FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo(); + FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); - unsigned DiagID = diag::err_typecheck_invalid_operands; - if ((OperationKind == ACK_Arithmetic) && - ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || - (RHSBuiltinTy && RHSBuiltinTy->isSVEBool()))) { - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); + return ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall(); + } } + return false; +} - if (Context.hasSameType(LHSType, RHSType)) - return LHSType; +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool checkBoundsSafetyFunctionPointerForAssignment(Sema &S, + QualType LHSType, + QualType RHSType) { - if (LHSType->isSveVLSBuiltinType() && !RHSType->isSveVLSBuiltinType()) { - if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) - return LHSType; - } - if (RHSType->isSveVLSBuiltinType() && !LHSType->isSveVLSBuiltinType()) { - if (LHS.get()->isLValue() || - !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) - return RHSType; - } + auto GetFunctionBaseAndProto = [](QualType Ty, const FunctionType *&Base, + const FunctionProtoType *&Proto) { + if (!Ty->isFunctionPointerType()) { + Base = nullptr; + Proto = nullptr; + return; + } + auto PTy = Ty->getAs(); + Base = PTy->getPointeeType()->getAs(); + Proto = dyn_cast(Base); + }; - if ((!LHSType->isSveVLSBuiltinType() && !LHSType->isRealType()) || - (!RHSType->isSveVLSBuiltinType() && !RHSType->isRealType())) { - Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + const FunctionType *LBase; + const FunctionType *RBase; + const FunctionProtoType *LProto; + const FunctionProtoType *RProto; - if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && - Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != - Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC) { - Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + GetFunctionBaseAndProto(LHSType, LBase, LProto); + GetFunctionBaseAndProto(RHSType, RBase, RProto); - if (LHSType->isSveVLSBuiltinType() || RHSType->isSveVLSBuiltinType()) { - QualType Scalar = LHSType->isSveVLSBuiltinType() ? RHSType : LHSType; - QualType Vector = LHSType->isSveVLSBuiltinType() ? LHSType : RHSType; - bool ScalarOrVector = - LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType(); + if (!LBase || !RBase) + return true; - Diag(Loc, diag::err_typecheck_vector_not_convertable_implict_truncation) - << ScalarOrVector << Scalar << Vector; + // Check return types + // Return types are covariant. + if (!S.getASTContext().hasCompatibleBoundsSafetyPointerLayout( + LBase->getReturnType(), RBase->getReturnType())) + return false; - return QualType(); + // Check parameter types + unsigned LNumParams = LProto ? LProto->getNumParams() : 0; + unsigned RNumParams = RProto ? RProto->getNumParams() : 0; + unsigned MinNumParams = LNumParams <= RNumParams ? LNumParams : RNumParams; + + for (unsigned i = 0; i < MinNumParams; ++i) { + // We switch left and right for the compatibility check because + // function parameters are contravariant. + // I.e., if R -> L then 'f(L)' -> 'f(R)' + if (!S.getASTContext().hasCompatibleBoundsSafetyPointerLayout( + RProto->getParamType(i), LProto->getParamType(i))) + return false; } - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); + auto CheckRemaningParams = [MinNumParams](const FunctionProtoType *Proto) { + if (!Proto || Proto->getNumParams() == MinNumParams) + return true; + + auto HasBoundsSafetyAttributeRecursive = [](QualType Ty) { + auto PT = Ty->getAs(); + while (PT) { + if (!PT->hasRawPointerLayout()) + return true; + PT = PT->getPointeeType()->getAs(); + } + return false; + }; + + for (unsigned i = MinNumParams; i < Proto->getNumParams(); ++i) + if (HasBoundsSafetyAttributeRecursive(Proto->getParamType(i))) + return false; + return true; + }; + + return CheckRemaningParams(LProto) || CheckRemaningParams(RProto); } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -// checkArithmeticNull - Detect when a NULL constant is used improperly in an -// expression. These are mainly cases where the null pointer is used as an -// integer instead of a pointer. -static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompare) { - // The canonical way to check for a GNU null is with isNullPointerConstant, - // but we use a bit of a hack here for speed; this is a relatively - // hot path, and isNullPointerConstant is slow. - bool LHSNull = isa(LHS.get()->IgnoreParenImpCasts()); - bool RHSNull = isa(RHS.get()->IgnoreParenImpCasts()); +// checkPointerTypesForAssignment - This is a very tricky routine (despite +// being closely modeled after the C99 spec:-). The odd characteristic of this +// routine is it effectively iqnores the qualifiers on the top level pointee. +// This circumvents the usual type rules specified in 6.2.7p1 & 6.7.5.[1-3]. +// FIXME: add a couple examples in this comment. +static Sema::AssignConvertType +checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, + SourceLocation Loc) { + assert(LHSType.isCanonical() && "LHS not canonicalized!"); + assert(RHSType.isCanonical() && "RHS not canonicalized!"); - QualType NonNullType = LHSNull ? RHS.get()->getType() : LHS.get()->getType(); + // get the "pointed to" type (ignoring qualifiers at the top level) + const Type *lhptee, *rhptee; + Qualifiers lhq, rhq; + std::tie(lhptee, lhq) = + cast(LHSType)->getPointeeType().split().asPair(); + std::tie(rhptee, rhq) = + cast(RHSType)->getPointeeType().split().asPair(); - // Avoid analyzing cases where the result will either be invalid (and - // diagnosed as such) or entirely valid and not something to warn about. - if ((!LHSNull && !RHSNull) || NonNullType->isBlockPointerType() || - NonNullType->isMemberPointerType() || NonNullType->isFunctionType()) - return; + Sema::AssignConvertType ConvTy = Sema::Compatible; - // Comparison operations would not make sense with a null pointer no matter - // what the other expression is. - if (!IsCompare) { - S.Diag(Loc, diag::warn_null_in_arithmetic_operation) - << (LHSNull ? LHS.get()->getSourceRange() : SourceRange()) - << (RHSNull ? RHS.get()->getSourceRange() : SourceRange()); - return; - } + // C99 6.5.16.1p1: This following citation is common to constraints + // 3 & 4 (below). ...and the type *pointed to* by the left has all the + // qualifiers of the type *pointed to* by the right; - // The rest of the operations only make sense with a null pointer - // if the other expression is a pointer. - if (LHSNull == RHSNull || NonNullType->isAnyPointerType() || - NonNullType->canDecayToPointerType()) - return; + // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. + if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && + lhq.compatiblyIncludesObjCLifetime(rhq)) { + // Ignore lifetime for further calculation. + lhq.removeObjCLifetime(); + rhq.removeObjCLifetime(); + } - S.Diag(Loc, diag::warn_null_in_comparison_operation) - << LHSNull /* LHS is NULL */ << NonNullType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); -} + if (!lhq.compatiblyIncludes(rhq)) { + // Treat address-space mismatches as fatal. + if (!lhq.isAddressSpaceSupersetOf(rhq)) + return Sema::IncompatiblePointerDiscardsQualifiers; -static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, - SourceLocation Loc) { - const auto *LUE = dyn_cast(LHS); - const auto *RUE = dyn_cast(RHS); - if (!LUE || !RUE) - return; - if (LUE->getKind() != UETT_SizeOf || LUE->isArgumentType() || - RUE->getKind() != UETT_SizeOf) - return; + // It's okay to add or remove GC or lifetime qualifiers when converting to + // and from void*. + else if (lhq.withoutObjCGCAttr().withoutObjCLifetime() + .compatiblyIncludes( + rhq.withoutObjCGCAttr().withoutObjCLifetime()) + && (lhptee->isVoidType() || rhptee->isVoidType())) + ; // keep old - const Expr *LHSArg = LUE->getArgumentExpr()->IgnoreParens(); - QualType LHSTy = LHSArg->getType(); - QualType RHSTy; + // Treat lifetime mismatches as fatal. + else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - if (RUE->isArgumentType()) - RHSTy = RUE->getArgumentType().getNonReferenceType(); - else - RHSTy = RUE->getArgumentExpr()->IgnoreParens()->getType(); + // Treat pointer-auth mismatches as fatal. + else if (lhq.getPointerAuth() != rhq.getPointerAuth()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - if (LHSTy->isPointerType() && !RHSTy->isPointerType()) { - if (!S.Context.hasSameUnqualifiedType(LHSTy->getPointeeType(), RHSTy)) - return; + // For GCC/MS compatibility, other qualifier mismatches are treated + // as still compatible in C. + else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; + } - S.Diag(Loc, diag::warn_division_sizeof_ptr) << LHS << LHS->getSourceRange(); - if (const auto *DRE = dyn_cast(LHSArg)) { - if (const ValueDecl *LHSArgDecl = DRE->getDecl()) - S.Diag(LHSArgDecl->getLocation(), diag::note_pointer_declared_here) - << LHSArgDecl; - } - } else if (const auto *ArrayTy = S.Context.getAsArrayType(LHSTy)) { - QualType ArrayElemTy = ArrayTy->getElementType(); - if (ArrayElemTy != S.Context.getBaseElementType(ArrayTy) || - ArrayElemTy->isDependentType() || RHSTy->isDependentType() || - RHSTy->isReferenceType() || ArrayElemTy->isCharType() || - S.Context.getTypeSize(ArrayElemTy) == S.Context.getTypeSize(RHSTy)) - return; - S.Diag(Loc, diag::warn_division_sizeof_array) - << LHSArg->getSourceRange() << ArrayElemTy << RHSTy; - if (const auto *DRE = dyn_cast(LHSArg)) { - if (const ValueDecl *LHSArgDecl = DRE->getDecl()) - S.Diag(LHSArgDecl->getLocation(), diag::note_array_declared_here) - << LHSArgDecl; - } + // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or + // incomplete type and the other is a pointer to a qualified or unqualified + // version of void... + if (lhptee->isVoidType()) { + if (rhptee->isIncompleteOrObjectType()) + return ConvTy; - S.Diag(Loc, diag::note_precedence_silence) << RHS; + // As an extension, we allow cast to/from void* to function pointer. + assert(rhptee->isFunctionType()); + return Sema::FunctionVoidPointer; } -} -static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, bool IsDiv) { - // Check for division/remainder by zero. - Expr::EvalResult RHSValue; - if (!RHS.get()->isValueDependent() && - RHS.get()->EvaluateAsInt(RHSValue, S.Context) && - RHSValue.Val.getInt() == 0) - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_remainder_division_by_zero) - << IsDiv << RHS.get()->getSourceRange()); -} + if (rhptee->isVoidType()) { + if (lhptee->isIncompleteOrObjectType()) + return ConvTy; -QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign, bool IsDiv) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + // As an extension, we allow cast to/from void* to function pointer. + assert(lhptee->isFunctionType()); + return Sema::FunctionVoidPointer; + } - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - if (LHSTy->isVectorType() || RHSTy->isVectorType()) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (LHSTy->isSveVLSBuiltinType() || RHSTy->isSveVLSBuiltinType()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_Arithmetic); - if (!IsDiv && - (LHSTy->isConstantMatrixType() || RHSTy->isConstantMatrixType())) - return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); - // For division, only matrix-by-scalar is supported. Other combinations with - // matrix types are invalid. - if (IsDiv && LHSTy->isConstantMatrixType() && RHSTy->isArithmeticType()) - return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); + if (!S.Diags.isIgnored( + diag::warn_typecheck_convert_incompatible_function_pointer_strict, + Loc) && + RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType() && + !S.IsFunctionConversion(RHSType, LHSType, RHSType)) + return Sema::IncompatibleFunctionPointerStrict; - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or + // unqualified versions of compatible types, ... + QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); + if (!S.Context.typesAreCompatible(ltrans, rtrans, + // TO_UPSTREAM(BoundsSafety) + /*CompareUnqualified=*/false)) { + // Check if the pointee types are compatible ignoring the sign. + // We explicitly check for char so that we catch "char" vs + // "unsigned char" on systems where "char" is unsigned. + if (lhptee->isCharType()) + ltrans = S.Context.UnsignedCharTy; + else if (lhptee->hasSignedIntegerRepresentation()) + ltrans = S.Context.getCorrespondingUnsignedType(ltrans); + if (rhptee->isCharType()) + rtrans = S.Context.UnsignedCharTy; + else if (rhptee->hasSignedIntegerRepresentation()) + rtrans = S.Context.getCorrespondingUnsignedType(rtrans); - if (compType.isNull() || !compType->isArithmeticType()) - return InvalidOperands(Loc, LHS, RHS); - if (IsDiv) { - DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); - DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc); + if (ltrans == rtrans) { + // Types are compatible ignoring the sign. Qualifier incompatibility + // takes priority over sign incompatibility because the sign + // warning can be disabled. + if (ConvTy != Sema::Compatible) + return ConvTy; + + return Sema::IncompatiblePointerSign; + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + const Type *lht = lhptee; + const Type *rht = rhptee; + for (;;) { + const auto *lhpt = dyn_cast(lht->getBaseElementTypeUnsafe()); + const auto *rhpt = dyn_cast(rht->getBaseElementTypeUnsafe()); + if (!lhpt || !rhpt) + break; + if (!BoundsSafetyPointerAttributes::areCompatible( + lhpt->getPointerAttributes(), rhpt->getPointerAttributes())) { + return Sema::IncompatibleNestedBoundsSafetyPointerAttributes; + } + lht = lhpt->getPointeeType().getTypePtr(); + rht = rhpt->getPointeeType().getTypePtr(); + } + + auto hasNestedBoundsSafetyAttribute = [](const Type *ptee) -> bool { + while (const auto *pt = + dyn_cast(ptee->getBaseElementTypeUnsafe())) { + // FIXME: Should not trigger an error for auto-bound type yet this will + // be relevant once we have a tree transform to fix auto bound types. + // rdar://71269324 + if (!pt->hasRawPointerLayout()) + return true; + ptee = pt->getPointeeType().getTypePtr(); + } + return false; + }; + if (S.getLangOpts().BoundsSafety && (hasNestedBoundsSafetyAttribute(lht) || + hasNestedBoundsSafetyAttribute(rht))) { + return Sema::IncompatibleNestedBoundsSafetyPointerAttributes; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + // If we are a multi-level pointer, it's possible that our issue is simply + // one of qualification - e.g. char ** -> const char ** is not allowed. If + // the eventual target type is the same and the pointers have the same + // level of indirection, this must be the issue. + if (isa(lhptee) && isa(rhptee)) { + do { + std::tie(lhptee, lhq) = + cast(lhptee)->getPointeeType().split().asPair(); + std::tie(rhptee, rhq) = + cast(rhptee)->getPointeeType().split().asPair(); + + // Inconsistent address spaces at this point is invalid, even if the + // address spaces would be compatible. + // FIXME: This doesn't catch address space mismatches for pointers of + // different nesting levels, like: + // __local int *** a; + // int ** b = a; + // It's not clear how to actually determine when such pointers are + // invalidly incompatible. + if (lhq.getAddressSpace() != rhq.getAddressSpace()) + return Sema::IncompatibleNestedPointerAddressSpaceMismatch; + + } while (isa(lhptee) && isa(rhptee)); + + if (lhptee == rhptee) + return Sema::IncompatibleNestedPointerQualifiers; + } + + // General pointer incompatibility takes priority over qualifiers. + if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType()) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !checkBoundsSafetyFunctionPointerForAssignment(S, LHSType, RHSType)) + return Sema::IncompatibleBoundsSafetyFunctionPointer; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return Sema::IncompatibleFunctionPointer; + } + return Sema::IncompatiblePointer; } - return compType; + if (!S.getLangOpts().CPlusPlus && + S.IsFunctionConversion(ltrans, rtrans, ltrans)) + return Sema::IncompatibleFunctionPointer; + if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) + return Sema::IncompatibleFunctionPointer; + if (S.IsInvalidSMECallConversion(rtrans, ltrans)) + return Sema::IncompatibleFunctionPointer; + return ConvTy; } -QualType Sema::CheckRemainderOperands( - ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); +/// checkBlockPointerTypesForAssignment - This routine determines whether two +/// block pointer types are compatible or whether a block and normal pointer +/// are compatible. It is more restrict than comparing two function pointer +// types. +static Sema::AssignConvertType +checkBlockPointerTypesForAssignment(Sema &S, QualType LHSType, + QualType RHSType) { + assert(LHSType.isCanonical() && "LHS not canonicalized!"); + assert(RHSType.isCanonical() && "RHS not canonicalized!"); - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - return InvalidOperands(Loc, LHS, RHS); + QualType lhptee, rhptee; + + // get the "pointed to" type (ignoring qualifiers at the top level) + lhptee = cast(LHSType)->getPointeeType(); + rhptee = cast(RHSType)->getPointeeType(); + + // In C++, the types have to match exactly. + if (S.getLangOpts().CPlusPlus) + return Sema::IncompatibleBlockPointer; + + Sema::AssignConvertType ConvTy = Sema::Compatible; + + // For blocks we enforce that qualifiers are identical. + Qualifiers LQuals = lhptee.getLocalQualifiers(); + Qualifiers RQuals = rhptee.getLocalQualifiers(); + if (S.getLangOpts().OpenCL) { + LQuals.removeAddressSpace(); + RQuals.removeAddressSpace(); } + if (LQuals != RQuals) + ConvTy = Sema::CompatiblePointerDiscardsQualifiers; - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_Arithmetic); + // FIXME: OpenCL doesn't define the exact compile time semantics for a block + // assignment. + // The current behavior is similar to C++ lambdas. A block might be + // assigned to a variable iff its return type and parameters are compatible + // (C99 6.2.7) with the corresponding return type and parameters of the LHS of + // an assignment. Presumably it should behave in way that a function pointer + // assignment does in C, so for each parameter and return type: + // * CVR and address space of LHS should be a superset of CVR and address + // space of RHS. + // * unqualified types should be compatible. + if (S.getLangOpts().OpenCL) { + if (!S.Context.typesAreBlockPointerCompatible( + S.Context.getQualifiedType(LHSType.getUnqualifiedType(), LQuals), + S.Context.getQualifiedType(RHSType.getUnqualifiedType(), RQuals))) + return Sema::IncompatibleBlockPointer; + } else if (!S.Context.typesAreBlockPointerCompatible(LHSType, RHSType)) + return Sema::IncompatibleBlockPointer; - return InvalidOperands(Loc, LHS, RHS); + return ConvTy; +} + +/// checkObjCPointerTypesForAssignment - Compares two objective-c pointer types +/// for assignment compatibility. +static Sema::AssignConvertType +checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, + QualType RHSType) { + assert(LHSType.isCanonical() && "LHS was not canonicalized!"); + assert(RHSType.isCanonical() && "RHS was not canonicalized!"); + + if (LHSType->isObjCBuiltinType()) { + // Class is not compatible with ObjC object pointers. + if (LHSType->isObjCClassType() && !RHSType->isObjCBuiltinType() && + !RHSType->isObjCQualifiedClassType()) + return Sema::IncompatiblePointer; + return Sema::Compatible; + } + if (RHSType->isObjCBuiltinType()) { + if (RHSType->isObjCClassType() && !LHSType->isObjCBuiltinType() && + !LHSType->isObjCQualifiedClassType()) + return Sema::IncompatiblePointer; + return Sema::Compatible; } + QualType lhptee = LHSType->castAs()->getPointeeType(); + QualType rhptee = RHSType->castAs()->getPointeeType(); - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + if (!lhptee.isAtLeastAsQualifiedAs(rhptee) && + // make an exception for id

+ !LHSType->isObjCQualifiedIdType()) + return Sema::CompatiblePointerDiscardsQualifiers; - if (compType.isNull() || !compType->isIntegerType()) - return InvalidOperands(Loc, LHS, RHS); - DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); - return compType; + if (S.Context.typesAreCompatible(LHSType, RHSType)) + return Sema::Compatible; + if (LHSType->isObjCQualifiedIdType() || RHSType->isObjCQualifiedIdType()) + return Sema::IncompatibleObjCQualifiedId; + return Sema::IncompatiblePointer; } -/// Diagnose invalid arithmetic on two void pointers. -static void diagnoseArithmeticOnTwoVoidPointers(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_void_type - : diag::ext_gnu_void_ptr) - << 1 /* two pointers */ << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); +Sema::AssignConvertType +Sema::CheckAssignmentConstraints(SourceLocation Loc, + QualType LHSType, QualType RHSType) { + // Fake up an opaque expression. We don't actually care about what + // cast operations are required, so if CheckAssignmentConstraints + // adds casts to this they'll be wasted, but fortunately that doesn't + // usually happen on valid code. + OpaqueValueExpr RHSExpr(Loc, RHSType, VK_PRValue); + ExprResult RHSPtr = &RHSExpr; + CastKind K; + + return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false); } -/// Diagnose invalid arithmetic on a void pointer. -static void diagnoseArithmeticOnVoidPointer(Sema &S, SourceLocation Loc, - Expr *Pointer) { - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_void_type - : diag::ext_gnu_void_ptr) - << 0 /* one pointer */ << Pointer->getSourceRange(); +/// This helper function returns true if QT is a vector type that has element +/// type ElementType. +static bool isVector(QualType QT, QualType ElementType) { + if (const VectorType *VT = QT->getAs()) + return VT->getElementType().getCanonicalType() == ElementType; + return false; } -/// Diagnose invalid arithmetic on a null pointer. +/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently +/// has code to accommodate several GCC extensions when type checking +/// pointers. Here are some objectionable examples that GCC considers warnings: /// -/// If \p IsGNUIdiom is true, the operation is using the 'p = (i8*)nullptr + n' -/// idiom, which we recognize as a GNU extension. +/// int a, *pint; +/// short *pshort; +/// struct foo *pfoo; /// -static void diagnoseArithmeticOnNullPointer(Sema &S, SourceLocation Loc, - Expr *Pointer, bool IsGNUIdiom) { - if (IsGNUIdiom) - S.Diag(Loc, diag::warn_gnu_null_ptr_arith) - << Pointer->getSourceRange(); - else - S.Diag(Loc, diag::warn_pointer_arith_null_ptr) - << S.getLangOpts().CPlusPlus << Pointer->getSourceRange(); -} - -/// Diagnose invalid subraction on a null pointer. -/// -static void diagnoseSubtractionOnNullPointer(Sema &S, SourceLocation Loc, - Expr *Pointer, bool BothNull) { - // Null - null is valid in C++ [expr.add]p7 - if (BothNull && S.getLangOpts().CPlusPlus) - return; - - // Is this s a macro from a system header? - if (S.Diags.getSuppressSystemWarnings() && S.SourceMgr.isInSystemMacro(Loc)) - return; - - S.DiagRuntimeBehavior(Loc, Pointer, - S.PDiag(diag::warn_pointer_sub_null_ptr) - << S.getLangOpts().CPlusPlus - << Pointer->getSourceRange()); -} - -/// Diagnose invalid arithmetic on two function pointers. -static void diagnoseArithmeticOnTwoFunctionPointers(Sema &S, SourceLocation Loc, - Expr *LHS, Expr *RHS) { - assert(LHS->getType()->isAnyPointerType()); - assert(RHS->getType()->isAnyPointerType()); - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_function_type - : diag::ext_gnu_ptr_func_arith) - << 1 /* two pointers */ << LHS->getType()->getPointeeType() - // We only show the second type if it differs from the first. - << (unsigned)!S.Context.hasSameUnqualifiedType(LHS->getType(), - RHS->getType()) - << RHS->getType()->getPointeeType() - << LHS->getSourceRange() << RHS->getSourceRange(); -} - -/// Diagnose invalid arithmetic on a function pointer. -static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, - Expr *Pointer) { - assert(Pointer->getType()->isAnyPointerType()); - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_function_type - : diag::ext_gnu_ptr_func_arith) - << 0 /* one pointer */ << Pointer->getType()->getPointeeType() - << 0 /* one pointer, so only one type */ - << Pointer->getSourceRange(); -} - -/// Emit error if Operand is incomplete pointer type -/// -/// \returns True if pointer has incomplete type -static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, - Expr *Operand) { - QualType ResType = Operand->getType(); - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); - - assert(ResType->isAnyPointerType()); - QualType PointeeTy = ResType->getPointeeType(); - return S.RequireCompleteSizedType( - Loc, PointeeTy, - diag::err_typecheck_arithmetic_incomplete_or_sizeless_type, - Operand->getSourceRange()); -} - -/// Check the validity of an arithmetic pointer operand. +/// pint = pshort; // warning: assignment from incompatible pointer type +/// a = pint; // warning: assignment makes integer from pointer without a cast +/// pint = a; // warning: assignment makes pointer from integer without a cast +/// pint = pfoo; // warning: assignment from incompatible pointer type /// -/// If the operand has pointer type, this code will check for pointer types -/// which are invalid in arithmetic operations. These will be diagnosed -/// appropriately, including whether or not the use is supported as an -/// extension. +/// As a result, the code for dealing with pointers is more complex than the +/// C99 spec dictates. /// -/// \returns True when the operand is valid to use (even if as an extension). -static bool checkArithmeticOpPointerOperand(Sema &S, SourceLocation Loc, - Expr *Operand) { - QualType ResType = Operand->getType(); - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); +/// Sets 'Kind' for any result kind except Incompatible. +Sema::AssignConvertType +Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, + CastKind &Kind, bool ConvertRHS) { + QualType RHSType = RHS.get()->getType(); + QualType OrigLHSType = LHSType; - if (!ResType->isAnyPointerType()) return true; + // Get canonical types. We're not formatting these types, just comparing + // them. + LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); + RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType(); - QualType PointeeTy = ResType->getPointeeType(); - if (PointeeTy->isVoidType()) { - diagnoseArithmeticOnVoidPointer(S, Loc, Operand); - return !S.getLangOpts().CPlusPlus; - } - if (PointeeTy->isFunctionType()) { - diagnoseArithmeticOnFunctionPointer(S, Loc, Operand); - return !S.getLangOpts().CPlusPlus; + // Common case: no conversion required. + if (LHSType == RHSType) { + Kind = CK_NoOp; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety && + !Context.canMergeTypeBounds(OrigLHSType, RHS.get()->getType())) { + Kind = CK_BoundsSafetyPointerCast; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } else { + return Compatible; + } } - if (checkArithmeticIncompletePointerType(S, Loc, Operand)) return false; - - return true; -} - -/// Check the validity of a binary arithmetic operation w.r.t. pointer -/// operands. -/// -/// This routine will diagnose any invalid arithmetic on pointer operands much -/// like \see checkArithmeticOpPointerOperand. However, it has special logic -/// for emitting a single diagnostic even for operations where both LHS and RHS -/// are (potentially problematic) pointers. -/// -/// \returns True when the operand is valid to use (even if as an extension). -static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - bool isLHSPointer = LHSExpr->getType()->isAnyPointerType(); - bool isRHSPointer = RHSExpr->getType()->isAnyPointerType(); - if (!isLHSPointer && !isRHSPointer) return true; - - QualType LHSPointeeTy, RHSPointeeTy; - if (isLHSPointer) LHSPointeeTy = LHSExpr->getType()->getPointeeType(); - if (isRHSPointer) RHSPointeeTy = RHSExpr->getType()->getPointeeType(); - - // if both are pointers check if operation is valid wrt address spaces - if (isLHSPointer && isRHSPointer) { - if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy)) { - S.Diag(Loc, - diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ - << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); - return false; + // If the LHS has an __auto_type, there are no additional type constraints + // to be worried about. + if (const auto *AT = dyn_cast(LHSType)) { + if (AT->isGNUAutoType()) { + Kind = CK_NoOp; + return Compatible; } } - // Check for arithmetic on pointers to incomplete types. - bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); - bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); - if (isLHSVoidPtr || isRHSVoidPtr) { - if (!isRHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, LHSExpr); - else if (!isLHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, RHSExpr); - else diagnoseArithmeticOnTwoVoidPointers(S, Loc, LHSExpr, RHSExpr); - - return !S.getLangOpts().CPlusPlus; + // If we have an atomic type, try a non-atomic assignment, then just add an + // atomic qualification step. + if (const AtomicType *AtomicTy = dyn_cast(LHSType)) { + Sema::AssignConvertType result = + CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); + if (result != Compatible) + return result; + if (Kind != CK_NoOp && ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind); + Kind = CK_NonAtomicToAtomic; + return Compatible; } - bool isLHSFuncPtr = isLHSPointer && LHSPointeeTy->isFunctionType(); - bool isRHSFuncPtr = isRHSPointer && RHSPointeeTy->isFunctionType(); - if (isLHSFuncPtr || isRHSFuncPtr) { - if (!isRHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, LHSExpr); - else if (!isLHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, - RHSExpr); - else diagnoseArithmeticOnTwoFunctionPointers(S, Loc, LHSExpr, RHSExpr); - - return !S.getLangOpts().CPlusPlus; + // If the left-hand side is a reference type, then we are in a + // (rare!) case where we've allowed the use of references in C, + // e.g., as a parameter type in a built-in function. In this case, + // just make sure that the type referenced is compatible with the + // right-hand side type. The caller is responsible for adjusting + // LHSType so that the resulting expression does not have reference + // type. + if (const ReferenceType *LHSTypeRef = LHSType->getAs()) { + if (Context.typesAreCompatible(LHSTypeRef->getPointeeType(), RHSType)) { + Kind = CK_LValueBitCast; + return Compatible; + } + return Incompatible; } - if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) - return false; - if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) - return false; - - return true; -} - -/// diagnoseStringPlusInt - Emit a warning when adding an integer to a string -/// literal. -static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - StringLiteral* StrExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); - Expr* IndexExpr = RHSExpr; - if (!StrExpr) { - StrExpr = dyn_cast(RHSExpr->IgnoreImpCasts()); - IndexExpr = LHSExpr; + // Allow scalar to ExtVector assignments, and assignments of an ExtVector type + // to the same ExtVector type. + if (LHSType->isExtVectorType()) { + if (RHSType->isExtVectorType()) + return Incompatible; + if (RHSType->isArithmeticType()) { + // CK_VectorSplat does T -> vector T, so first cast to the element type. + if (ConvertRHS) + RHS = prepareVectorSplat(LHSType, RHS.get()); + Kind = CK_VectorSplat; + return Compatible; + } } - bool IsStringPlusInt = StrExpr && - IndexExpr->getType()->isIntegralOrUnscopedEnumerationType(); - if (!IsStringPlusInt || IndexExpr->isValueDependent()) - return; + // Conversions to or from vector type. + if (LHSType->isVectorType() || RHSType->isVectorType()) { + if (LHSType->isVectorType() && RHSType->isVectorType()) { + // Allow assignments of an AltiVec vector type to an equivalent GCC + // vector type and vice versa + if (Context.areCompatibleVectorTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } - SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::warn_string_plus_int) - << DiagRange << IndexExpr->IgnoreImpCasts()->getType(); + // If we are allowing lax vector conversions, and LHS and RHS are both + // vectors, the total size only needs to be the same. This is a bitcast; + // no bits are changed but the result type is different. + if (isLaxVectorConversion(RHSType, LHSType)) { + // The default for lax vector conversions with Altivec vectors will + // change, so if we are converting between vector types where + // at least one is an Altivec vector, emit a warning. + if (Context.getTargetInfo().getTriple().isPPC() && + anyAltivecTypes(RHSType, LHSType) && + !Context.areCompatibleVectorTypes(RHSType, LHSType)) + Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) + << RHSType << LHSType; + Kind = CK_BitCast; + return IncompatibleVectors; + } + } - // Only print a fixit for "str" + int, not for int + "str". - if (IndexExpr == RHSExpr) { - SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) - << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") - << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") - << FixItHint::CreateInsertion(EndLoc, "]"); - } else - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); -} - -/// Emit a warning when adding a char literal to a string. -static void diagnoseStringPlusChar(Sema &Self, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - const Expr *StringRefExpr = LHSExpr; - const CharacterLiteral *CharExpr = - dyn_cast(RHSExpr->IgnoreImpCasts()); - - if (!CharExpr) { - CharExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); - StringRefExpr = RHSExpr; - } + // When the RHS comes from another lax conversion (e.g. binops between + // scalars and vectors) the result is canonicalized as a vector. When the + // LHS is also a vector, the lax is allowed by the condition above. Handle + // the case where LHS is a scalar. + if (LHSType->isScalarType()) { + const VectorType *VecType = RHSType->getAs(); + if (VecType && VecType->getNumElements() == 1 && + isLaxVectorConversion(RHSType, LHSType)) { + if (Context.getTargetInfo().getTriple().isPPC() && + (VecType->getVectorKind() == VectorKind::AltiVecVector || + VecType->getVectorKind() == VectorKind::AltiVecBool || + VecType->getVectorKind() == VectorKind::AltiVecPixel)) + Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) + << RHSType << LHSType; + ExprResult *VecExpr = &RHS; + *VecExpr = ImpCastExprToType(VecExpr->get(), LHSType, CK_BitCast); + Kind = CK_BitCast; + return Compatible; + } + } - if (!CharExpr || !StringRefExpr) - return; + // Allow assignments between fixed-length and sizeless SVE vectors. + if ((LHSType->isSVESizelessBuiltinType() && RHSType->isVectorType()) || + (LHSType->isVectorType() && RHSType->isSVESizelessBuiltinType())) + if (Context.areCompatibleSveTypes(LHSType, RHSType) || + Context.areLaxCompatibleSveTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } - const QualType StringType = StringRefExpr->getType(); + // Allow assignments between fixed-length and sizeless RVV vectors. + if ((LHSType->isRVVSizelessBuiltinType() && RHSType->isVectorType()) || + (LHSType->isVectorType() && RHSType->isRVVSizelessBuiltinType())) { + if (Context.areCompatibleRVVTypes(LHSType, RHSType) || + Context.areLaxCompatibleRVVTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } + } - // Return if not a PointerType. - if (!StringType->isAnyPointerType()) - return; + return Incompatible; + } - // Return if not a CharacterType. - if (!StringType->getPointeeType()->isAnyCharacterType()) - return; + // Diagnose attempts to convert between __ibm128, __float128 and long double + // where such conversions currently can't be handled. + if (unsupportedTypeConversion(*this, LHSType, RHSType)) + return Incompatible; - ASTContext &Ctx = Self.getASTContext(); - SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + // Disallow assigning a _Complex to a real type in C++ mode since it simply + // discards the imaginary part. + if (getLangOpts().CPlusPlus && RHSType->getAs() && + !LHSType->getAs()) + return Incompatible; - const QualType CharType = CharExpr->getType(); - if (!CharType->isAnyCharacterType() && - CharType->isIntegerType() && - llvm::isUIntN(Ctx.getCharWidth(), CharExpr->getValue())) { - Self.Diag(OpLoc, diag::warn_string_plus_char) - << DiagRange << Ctx.CharTy; - } else { - Self.Diag(OpLoc, diag::warn_string_plus_char) - << DiagRange << CharExpr->getType(); + // Arithmetic conversions. + if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && + !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { + if (ConvertRHS) + Kind = PrepareScalarCast(RHS, LHSType); + return Compatible; } - // Only print a fixit for str + char, not for char + str. - if (isa(RHSExpr->IgnoreImpCasts())) { - SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) - << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") - << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") - << FixItHint::CreateInsertion(EndLoc, "]"); - } else { - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); - } -} + // Conversions to normal pointers. + if (const PointerType *LHSPointer = dyn_cast(LHSType)) { + // U* -> T* + if (const PointerType *RHSPointer = dyn_cast(RHSType)) { + LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); + LangAS AddrSpaceR = RHSPointer->getPointeeType().getAddressSpace(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + bool IsSrcNull = RHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull); + if (!IsSrcNull && !RHSType->isSafePointerType() && + !RHS.get()->getType()->isBoundsAttributedType() && + LHSType->isSafePointerType() && + !OrigLHSType->isValueTerminatedType()) { + return LHSPointer->isSingle() ? IncompatibleUnsafeToSafePointer + : IncompatibleUnsafeToIndexablePointer; + } + if (!IsSrcNull && RHSPointer->isSingle() && + LHSType->isPointerTypeWithBounds() && + !RHS.get()->getType()->isBoundsAttributedType()) { + if (RHSPointer->getPointeeType()->isIncompleteOrSizelessType()) + return IncompleteSingleToIndexablePointer; -/// Emit error when two pointers are incompatible. -static void diagnosePointerIncompatibility(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - assert(LHSExpr->getType()->isAnyPointerType()); - assert(RHSExpr->getType()->isAnyPointerType()); - S.Diag(Loc, diag::err_typecheck_sub_ptr_compatible) - << LHSExpr->getType() << RHSExpr->getType() << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); -} + DiagnoseSingleToWideLosingBounds(LHSType, RHSType, RHS.get()); + } + } -// C99 6.5.6 -QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - QualType* CompLHSTy) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + auto RHSFA = RHSPointer->getPointerAttributes(); + if (LHSPointer->getPointerAttributes() != RHSFA) { + // First convert with the left-hand type taking the -fbounds-safety + // attributes of the right hand type, and then do a + // BoundsSafetyPointerCast from the result of that to the left-hand type. + // This ensures that places that need two implicit casts (like + // `int *__single = (char *__bidi)x`) will first BitCast to + // `int *__bidi` and then FPC to `int *__single`, which will CodeGen + // the bounds check correctly. + auto LHSIntermediateType = Context.getPointerType( + LHSPointer->getPointeeType(), RHSFA); + auto Result = CheckAssignmentConstraints(LHSIntermediateType, RHS, + Kind, ConvertRHS); + bool SkipNoOp = Kind == CK_NoOp && + Context.hasSameType(LHSPointer->getPointeeType(), + RHSPointer->getPointeeType()); + if (ConvertRHS && !SkipNoOp) + RHS = ImpCastExprToType(RHS.get(), LHSIntermediateType, Kind); + Kind = CK_BoundsSafetyPointerCast; + + // Look for cases where a __single pointer is implicitly converted to an + // explicitly indexable pointer. We do this here rather than earlier in + // the function because returning earlier would cause the extra implicit + // cast logic above to be missed which would change the generated AST. + bool IsSrcNull = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + if (!IsSrcNull && RHSPointer->isSingle() && + !RHS.get()->getType()->isBoundsAttributedType()) { + bool IsExplicitlyBoundedPointer = LHSType->isPointerTypeWithBounds(); + if (OrigLHSType->hasAttr(attr::PtrAutoAttr)) { + // We avoid implicitly indexable (i.e. implicit __bidi_indexable + // local vars) because warning about these would likely be too + // noisy. + IsExplicitlyBoundedPointer = false; + } + if (IsExplicitlyBoundedPointer) { + assert(Kind == CK_BoundsSafetyPointerCast); + // Return this for the purposes of later emitting a warning. + return CompatibleSingleToExplicitIndexablePointer; + } + } - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - QualType compType = - CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (CompLHSTy) *CompLHSTy = compType; - return compType; - } + if (Result != Compatible) + return Result; + } else if (OrigLHSType->isBoundsAttributedType() && + RHSType->isSinglePointerType()) { + // Single to dynamic bounds pointer should first create an implicit cast + // to `__bidi_indexable` and then create any necessary cast such as + // bitcast in the following example. `void *__sized_by(len) dst = (int + // *__single)src` + assert(!RHS.get()->getType()->isBoundsAttributedType()); + if (ConvertRHS) { + QualType BidiRTy = Context.getPointerType( + RHSPointer->getPointeeType(), + BoundsSafetyPointerAttributes::bidiIndexable()); + RHS = ImpCastExprToType(RHS.get(), BidiRTy, CK_BoundsSafetyPointerCast); + auto Result = + CheckAssignmentConstraints(OrigLHSType, RHS, Kind, ConvertRHS); + if (Result != Compatible) + return Result; + } else { + Kind = CK_BitCast; + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } else if (AddrSpaceL != AddrSpaceR) + Kind = CK_AddressSpaceConversion; + else if (Context.hasCvrSimilarType(RHSType, LHSType)) + Kind = CK_NoOp; + else + Kind = CK_BitCast; + return checkPointerTypesForAssignment(*this, LHSType, RHSType, + RHS.get()->getBeginLoc()); + } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - QualType compType = - CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; - } + // int -> T* + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null? + return LHSPointer->isSafePointer() + ? IncompatibleIntToSafePointer : IntToPointer; + } - if (LHS.get()->getType()->isConstantMatrixType() || - RHS.get()->getType()->isConstantMatrixType()) { - QualType compType = - CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; - } + // C pointers are not compatible with ObjC object pointers, + // with two exceptions: + if (isa(RHSType)) { + // - conversions to void* + if (LHSPointer->getPointeeType()->isVoidType()) { + Kind = CK_BitCast; + return Compatible; + } - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // - conversions from 'Class' to the redefinition type + if (RHSType->isObjCClassType() && + Context.hasSameType(LHSType, + Context.getObjCClassRedefinitionType())) { + Kind = CK_BitCast; + return Compatible; + } - // Diagnose "string literal" '+' int and string '+' "char literal". - if (Opc == BO_Add) { - diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get()); - diagnoseStringPlusChar(*this, Loc, LHS.get(), RHS.get()); - } + Kind = CK_BitCast; + return IncompatiblePointer; + } - // handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { - if (CompLHSTy) *CompLHSTy = compType; - return compType; + // U^ -> void* + if (RHSType->getAs()) { + if (LHSPointer->getPointeeType()->isVoidType()) { + LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); + LangAS AddrSpaceR = RHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + Kind = + AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; + return Compatible; + } + } + + return Incompatible; } - // Type-checking. Ultimately the pointer's going to be in PExp; - // note that we bias towards the LHS being the pointer. - Expr *PExp = LHS.get(), *IExp = RHS.get(); + // Conversions to block pointers. + if (isa(LHSType)) { + // U^ -> T^ + if (RHSType->isBlockPointerType()) { + LangAS AddrSpaceL = LHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + LangAS AddrSpaceR = RHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; + return checkBlockPointerTypesForAssignment(*this, LHSType, RHSType); + } - bool isObjCPointer; - if (PExp->getType()->isPointerType()) { - isObjCPointer = false; - } else if (PExp->getType()->isObjCObjectPointerType()) { - isObjCPointer = true; - } else { - std::swap(PExp, IExp); - if (PExp->getType()->isPointerType()) { - isObjCPointer = false; - } else if (PExp->getType()->isObjCObjectPointerType()) { - isObjCPointer = true; - } else { - return InvalidOperands(Loc, LHS, RHS); + // int or null -> T^ + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null + return IntToBlockPointer; } - } - assert(PExp->getType()->isAnyPointerType()); - if (!IExp->getType()->isIntegerType()) - return InvalidOperands(Loc, LHS, RHS); + // id -> T^ + if (getLangOpts().ObjC && RHSType->isObjCIdType()) { + Kind = CK_AnyPointerToBlockPointerCast; + return Compatible; + } - // Adding to a null pointer results in undefined behavior. - if (PExp->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull)) { - // In C++ adding zero to a null pointer is defined. - Expr::EvalResult KnownVal; - if (!getLangOpts().CPlusPlus || - (!IExp->isValueDependent() && - (!IExp->EvaluateAsInt(KnownVal, Context) || - KnownVal.Val.getInt() != 0))) { - // Check the conditions to see if this is the 'p = nullptr + n' idiom. - bool IsGNUIdiom = BinaryOperator::isNullPointerArithmeticExtension( - Context, BO_Add, PExp, IExp); - diagnoseArithmeticOnNullPointer(*this, Loc, PExp, IsGNUIdiom); - } - } - - if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) - return QualType(); - - if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) - return QualType(); + // void* -> T^ + if (const PointerType *RHSPT = RHSType->getAs()) + if (RHSPT->getPointeeType()->isVoidType()) { + Kind = CK_AnyPointerToBlockPointerCast; + return Compatible; + } - // Arithmetic on label addresses is normally allowed, except when we add - // a ptrauth signature to the addresses. - if (isa(PExp) && getLangOpts().PointerAuthIndirectGotos) { - Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) - << /*addition*/ 1; - return QualType(); + return Incompatible; } - // Check array bounds for pointer arithemtic - CheckArrayAccess(PExp, IExp); + // Conversions to Objective-C pointers. + if (isa(LHSType)) { + // A* -> B* + if (RHSType->isObjCObjectPointerType()) { + Kind = CK_BitCast; + Sema::AssignConvertType result = + checkObjCPointerTypesForAssignment(*this, LHSType, RHSType); + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + result == Compatible && + !ObjC().CheckObjCARCUnavailableWeakConversion(OrigLHSType, RHSType)) + result = IncompatibleObjCWeakRef; + return result; + } - if (CompLHSTy) { - QualType LHSTy = Context.isPromotableBitField(LHS.get()); - if (LHSTy.isNull()) { - LHSTy = LHS.get()->getType(); - if (Context.isPromotableIntegerType(LHSTy)) - LHSTy = Context.getPromotedIntegerType(LHSTy); + // int or null -> A* + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null + return IntToPointer; } - *CompLHSTy = LHSTy; - } - return PExp->getType(); -} + // In general, C pointers are not compatible with ObjC object pointers, + // with two exceptions: + if (isa(RHSType)) { + Kind = CK_CPointerToObjCPointerCast; -// C99 6.5.6 -QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - QualType* CompLHSTy) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + // - conversions from 'void*' + if (RHSType->isVoidPointerType()) { + return Compatible; + } - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - QualType compType = - CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (CompLHSTy) *CompLHSTy = compType; - return compType; - } + // - conversions to 'Class' from its redefinition type + if (LHSType->isObjCClassType() && + Context.hasSameType(RHSType, + Context.getObjCClassRedefinitionType())) { + return Compatible; + } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - QualType compType = - CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; + return IncompatiblePointer; + } + + // Only under strict condition T^ is compatible with an Objective-C pointer. + if (RHSType->isBlockPointerType() && + LHSType->isBlockCompatibleObjCPointerType(Context)) { + if (ConvertRHS) + maybeExtendBlockObject(RHS); + Kind = CK_BlockPointerToObjCPointerCast; + return Compatible; + } + + return Incompatible; } - if (LHS.get()->getType()->isConstantMatrixType() || - RHS.get()->getType()->isConstantMatrixType()) { - QualType compType = - CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; + // Conversion to nullptr_t (C23 only) + if (getLangOpts().C23 && LHSType->isNullPtrType() && + RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) { + // null -> nullptr_t + Kind = CK_NullToPointer; + return Compatible; } - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // Conversions from pointers that are not covered by the above. + if (isa(RHSType)) { + // T* -> _Bool + if (LHSType == Context.BoolTy) { + Kind = CK_PointerToBoolean; + return Compatible; + } - // Enforce type constraints: C99 6.5.6p3. + // T* -> int + if (LHSType->isIntegerType()) { + Kind = CK_PointerToIntegral; + return PointerToInt; + } - // Handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { - if (CompLHSTy) *CompLHSTy = compType; - return compType; + return Incompatible; } - // Either ptr - int or ptr - ptr. - if (LHS.get()->getType()->isAnyPointerType()) { - QualType lpointee = LHS.get()->getType()->getPointeeType(); + // Conversions from Objective-C pointers that are not covered by the above. + if (isa(RHSType)) { + // T* -> _Bool + if (LHSType == Context.BoolTy) { + Kind = CK_PointerToBoolean; + return Compatible; + } - // Diagnose bad cases where we step over interface counts. - if (LHS.get()->getType()->isObjCObjectPointerType() && - checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) - return QualType(); + // T* -> int + if (LHSType->isIntegerType()) { + Kind = CK_PointerToIntegral; + return PointerToInt; + } - // Arithmetic on label addresses is normally allowed, except when we add - // a ptrauth signature to the addresses. - if (isa(LHS.get()) && - getLangOpts().PointerAuthIndirectGotos) { - Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) - << /*subtraction*/ 0; - return QualType(); + return Incompatible; + } + + // struct A -> struct B + if (isa(LHSType) && isa(RHSType)) { + if (Context.typesAreCompatible(LHSType, RHSType)) { + Kind = CK_NoOp; + return Compatible; } + } - // The result type of a pointer-int computation is the pointer type. - if (RHS.get()->getType()->isIntegerType()) { - // Subtracting from a null pointer should produce a warning. - // The last argument to the diagnose call says this doesn't match the - // GNU int-to-pointer idiom. - if (LHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull)) { - // In C++ adding zero to a null pointer is defined. - Expr::EvalResult KnownVal; - if (!getLangOpts().CPlusPlus || - (!RHS.get()->isValueDependent() && - (!RHS.get()->EvaluateAsInt(KnownVal, Context) || - KnownVal.Val.getInt() != 0))) { - diagnoseArithmeticOnNullPointer(*this, Loc, LHS.get(), false); - } - } + if (LHSType->isSamplerT() && RHSType->isIntegerType()) { + Kind = CK_IntToOCLSampler; + return Compatible; + } - if (!checkArithmeticOpPointerOperand(*this, Loc, LHS.get())) - return QualType(); + return Incompatible; +} - // Check array bounds for pointer arithemtic - CheckArrayAccess(LHS.get(), RHS.get(), /*ArraySubscriptExpr*/nullptr, - /*AllowOnePastEnd*/true, /*IndexNegated*/true); +/* TO_UPSTREAM(BoundsSafety) ON*/ +void Sema::DiagnoseSingleToWideLosingBounds(QualType LHSType, + QualType RHSType, + const Expr *RHSExp) { + assert(LHSType->isPointerType() && RHSType->isPointerType()); + // We don't need to specialize it for structs with flexible array + // members because when LHSType is a flexible array member smaller + // than the source type, that is still a problem. + auto LHSSize = Context.getTypeSizeOrNull(LHSType->getPointeeType()); + auto RHSSize = Context.getTypeSizeOrNull(RHSType->getPointeeType()); - if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); - return LHS.get()->getType(); - } + if (LHSSize >= RHSSize) + return; - // Handle pointer-pointer subtractions. - if (const PointerType *RHSPTy - = RHS.get()->getType()->getAs()) { - QualType rpointee = RHSPTy->getPointeeType(); + const auto *LHSPointer = LHSType->getAs(); + QualType LHSPointeeTy = LHSPointer->getPointeeType(); + Diag(RHSExp->getExprLoc(), + diag::warn_bounds_safety_single_bitcast_lose_bounds) + << RHSType << LHSType << LHSPointer->isBidiIndexable() + << LHSPointeeTy << LHSPointeeTy->isIncompleteType(); - if (getLangOpts().CPlusPlus) { - // Pointee types must be the same: C++ [expr.add] - if (!Context.hasSameUnqualifiedType(lpointee, rpointee)) { - diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); - } - } else { - // Pointee types must be compatible C99 6.5.6p3 - if (!Context.typesAreCompatible( - Context.getCanonicalType(lpointee).getUnqualifiedType(), - Context.getCanonicalType(rpointee).getUnqualifiedType())) { - diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); - return QualType(); - } - } + QualType RHSTyBidi = Context.getBoundsSafetyPointerType(RHSType, + BoundsSafetyPointerAttributes::bidiIndexable()); + std::string RHSTyBidiStr = "(" + RHSTyBidi.getAsString() + ")"; - if (!checkArithmeticBinOpPointerOperands(*this, Loc, - LHS.get(), RHS.get())) - return QualType(); + CharSourceRange ExprRange = + CharSourceRange::getCharRange(RHSExp->getBeginLoc(), + getLocForEndOfToken(RHSExp->getExprLoc())); - bool LHSIsNullPtr = LHS.get()->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull); - bool RHSIsNullPtr = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull); + std::string ExprStr = "'" + + std::string(Lexer::getSourceText(ExprRange, getSourceManager(), getLangOpts())) + + "'"; - // Subtracting nullptr or from nullptr is suspect - if (LHSIsNullPtr) - diagnoseSubtractionOnNullPointer(*this, Loc, LHS.get(), RHSIsNullPtr); - if (RHSIsNullPtr) - diagnoseSubtractionOnNullPointer(*this, Loc, RHS.get(), LHSIsNullPtr); + Diag(RHSExp->getExprLoc(), + diag::note_bounds_safety_single_bitcast_lose_bounds_keep_bound) + << RHSTyBidi << ExprStr + << FixItHint::CreateInsertion(RHSExp->getBeginLoc(), RHSTyBidiStr); - // The pointee type may have zero size. As an extension, a structure or - // union may have zero size or an array may have zero length. In this - // case subtraction does not make sense. - if (!rpointee->isVoidType() && !rpointee->isFunctionType()) { - CharUnits ElementSize = Context.getTypeSizeInChars(rpointee); - if (ElementSize.isZero()) { - Diag(Loc,diag::warn_sub_ptr_zero_size_types) - << rpointee.getUnqualifiedType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - } - } + auto PD = PDiag(diag::note_bounds_safety_single_bitcast_lose_bounds_silence); - if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); - return Context.getPointerDiffType(); - } - } + if (!LHSType->getPointeeType()->isSizeMeaningless()) { - return InvalidOperands(Loc, LHS, RHS); + QualType LHSTySingle = Context.getBoundsSafetyPointerType(LHSType, + BoundsSafetyPointerAttributes::single()); + std::string LHSTySingleStr = "(" + LHSTySingle.getAsString() + ")"; + PD << FixItHint::CreateInsertion(RHSExp->getBeginLoc(), LHSTySingleStr); + } + Diag(RHSExp->getExprLoc(), PD); } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -static bool isScopedEnumerationType(QualType T) { - if (const EnumType *ET = T->getAs()) - return ET->getDecl()->isScoped(); - return false; +/// Constructs a transparent union from an expression that is +/// used to initialize the transparent union. +static void ConstructTransparentUnion(Sema &S, ASTContext &C, + ExprResult &EResult, QualType UnionType, + FieldDecl *Field) { + // Build an initializer list that designates the appropriate member + // of the transparent union. + Expr *E = EResult.get(); + InitListExpr *Initializer = new (C) InitListExpr(C, SourceLocation(), + E, SourceLocation()); + Initializer->setType(UnionType); + Initializer->setInitializedFieldInUnion(Field); + + // Build a compound literal constructing a value of the transparent + // union type from this initializer list. + TypeSourceInfo *unionTInfo = C.getTrivialTypeSourceInfo(UnionType); + EResult = new (C) CompoundLiteralExpr(SourceLocation(), unionTInfo, UnionType, + VK_PRValue, Initializer, false); } -static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - QualType LHSType) { - // OpenCL 6.3j: shift values are effectively % word size of LHS (more defined), - // so skip remaining warnings as we don't want to modify values within Sema. - if (S.getLangOpts().OpenCL) - return; +Sema::AssignConvertType +Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType, + ExprResult &RHS) { + QualType RHSType = RHS.get()->getType(); - // Check right/shifter operand - Expr::EvalResult RHSResult; - if (RHS.get()->isValueDependent() || - !RHS.get()->EvaluateAsInt(RHSResult, S.Context)) - return; - llvm::APSInt Right = RHSResult.Val.getInt(); + // If the ArgType is a Union type, we want to handle a potential + // transparent_union GCC extension. + const RecordType *UT = ArgType->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr()) + return Incompatible; - if (Right.isNegative()) { - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_shift_negative) - << RHS.get()->getSourceRange()); - return; - } + // The field to initialize within the transparent union. + RecordDecl *UD = UT->getDecl(); + FieldDecl *InitField = nullptr; + // It's compatible if the expression matches any of the fields. + for (auto *it : UD->fields()) { + if (it->getType()->isPointerType()) { + // If the transparent union contains a pointer type, we allow: + // 1) void pointer + // 2) null pointer constant + if (RHSType->isPointerType()) + if (RHSType->castAs()->getPointeeType()->isVoidType()) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), CK_BitCast); + InitField = it; + break; + } - QualType LHSExprType = LHS.get()->getType(); - uint64_t LeftSize = S.Context.getTypeSize(LHSExprType); - if (LHSExprType->isBitIntType()) - LeftSize = S.Context.getIntWidth(LHSExprType); - else if (LHSExprType->isFixedPointType()) { - auto FXSema = S.Context.getFixedPointSemantics(LHSExprType); - LeftSize = FXSema.getWidth() - (unsigned)FXSema.hasUnsignedPadding(); - } - if (Right.uge(LeftSize)) { - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_shift_gt_typewidth) - << RHS.get()->getSourceRange()); - return; + if (RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), + CK_NullToPointer); + InitField = it; + break; + } + } + + CastKind Kind; + if (CheckAssignmentConstraints(it->getType(), RHS, Kind) + == Compatible) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), Kind); + InitField = it; + break; + } } - // FIXME: We probably need to handle fixed point types specially here. - if (Opc != BO_Shl || LHSExprType->isFixedPointType()) - return; + if (!InitField) + return Incompatible; - // When left shifting an ICE which is signed, we can check for overflow which - // according to C++ standards prior to C++2a has undefined behavior - // ([expr.shift] 5.8/2). Unsigned integers have defined behavior modulo one - // more than the maximum value representable in the result type, so never - // warn for those. (FIXME: Unsigned left-shift overflow in a constant - // expression is still probably a bug.) - Expr::EvalResult LHSResult; - if (LHS.get()->isValueDependent() || - LHSType->hasUnsignedIntegerRepresentation() || - !LHS.get()->EvaluateAsInt(LHSResult, S.Context)) - return; - llvm::APSInt Left = LHSResult.Val.getInt(); + ConstructTransparentUnion(*this, Context, RHS, ArgType, InitField); + return Compatible; +} - // Don't warn if signed overflow is defined, then all the rest of the - // diagnostics will not be triggered because the behavior is defined. - // Also don't warn in C++20 mode (and newer), as signed left shifts - // always wrap and never overflow. - if (S.getLangOpts().isSignedOverflowDefined() || S.getLangOpts().CPlusPlus20) - return; +/* TO_UPSTREAM(BoundsSafety) ON*/ +// Check dependent variables that are used in a return type. Those variables +// don't have DependerDeclsAttr or __started_by() type, since currently we +// cannot express referring to function return (e.g., we cannot create +// __started_by(func ret)). +static bool checkDynamicBoundVariableEscapeInRetType(Sema &S, + const Expr *RHSExp, + const ValueDecl *VD, + const int ExpAddrOfLevel) { + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + if (!VD->isDependentParamOfReturnType(&RetType, &Info)) + return true; - // If LHS does not have a non-negative value then, the - // behavior is undefined before C++2a. Warn about it. - if (Left.isNegative()) { - S.DiagRuntimeBehavior(Loc, LHS.get(), - S.PDiag(diag::warn_shift_lhs_negative) - << LHS.get()->getSourceRange()); - return; - } + int AttAddrOfLevel = Info->isDeref() ? -1 : 0; + if (ExpAddrOfLevel <= AttAddrOfLevel) + return true; - llvm::APInt ResultBits = - static_cast(Right) + Left.getSignificantBits(); - if (ResultBits.ule(LeftSize)) - return; - llvm::APSInt Result = Left.extend(ResultBits.getLimitedValue()); - Result = Result.shl(Right); + const auto *CATy = dyn_cast(RetType); + BoundsAttributedType::BoundsAttrKind Kind = + CATy ? CATy->getKind() : BoundsAttributedType::BoundsAttrKind::EndedBy; + S.Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << Kind << /*variable*/ 0 << RHSExp->getSourceRange(); + return false; +} - // Print the bit representation of the signed integer as an unsigned - // hexadecimal number. - SmallString<40> HexResult; - Result.toString(HexResult, 16, /*Signed =*/false, /*Literal =*/true); +// BoundsSafety: a dynamic count variable cannot be pointed to by any other +// variable. Exception is when the variable is passed as a compatible argument +// to a function. +bool Sema::CheckDynamicBoundVariableEscape(QualType LHSType, Expr *RHSExp) { + RHSExp = RHSExp->IgnoreParenCasts(); + int ExpAddrOfLevel = 0; + auto VD = findValueDecl(RHSExp, &ExpAddrOfLevel); + auto BaseRecord = findBaseRecordDecl(RHSExp); + if (!VD) + return true; - // If we are only missing a sign bit, this is less likely to result in actual - // bugs -- if the result is cast back to an unsigned type, it will have the - // expected value. Thus we place this behind a different warning that can be - // turned off separately if needed. - if (ResultBits - 1 == LeftSize) { - S.Diag(Loc, diag::warn_shift_result_sets_sign_bit) - << HexResult << LHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return; + if (const auto *Att = VD->getAttr()) { + int AttAddrOfLevel = Att->getIsDeref() ? -1 : 0; + if (ExpAddrOfLevel > AttAddrOfLevel) { + assert(Att->dependerDecls_size() > 0); + for (const auto DepDecl : Att->dependerDecls()) { + /// If DepDecl is part of a count expression through a nested struct we + /// need to check whether the FAM exists in the current context. + /// Example of irrelevant dep decl (SimpleOuter::fam): + /// \code + /// struct SimpleInner { + /// int dummy; + /// int len; + /// }; + /// struct SimpleOuter { + /// struct SimpleInner hdr; + /// char fam[__counted_by(hdr.len)]; + /// }; + /// void set_len(struct SimpleInner * p) { + /// // struct SimpleInner doesn't contain any FAMs, so not checked + /// // -fbounds-safety doesn't allow taking address of + /// // SimpleOuter::hdr though, so this is never referred to by a + /// // FAM and thus safe + /// p->len = 2; + /// } + /// \endcode + if (BaseRecord && !BaseRecord->isParentStructOf(DepDecl)) + continue; + QualType DepTy = cast(DepDecl)->getType(); + auto *DCPTy = DepTy->getAs(); + if (!DCPTy) + DCPTy = DepTy->getPointeeType()->getAs(); + assert(DCPTy); + // Error has already been emitted for simply taking the address of the + // count field + if (BaseRecord && DCPTy->isIncompleteArrayType()) + continue; + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << DCPTy->getKind() << isa(VD) + << RHSExp->getSourceRange(); + return false; + } + } } - S.Diag(Loc, diag::warn_shift_result_gt_typewidth) - << HexResult.str() << Result.getSignificantBits() << LHSType - << Left.getBitWidth() << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + QualType Ty = RHSExp->getType(); + while (Ty->isPointerType()) { + Ty = Ty->getPointeeType(); + if (!Ty->isBoundsAttributedType()) + continue; + if (BaseRecord && Ty->isIncompleteArrayType()) + break; + if (const auto *DCPT = Ty->getAs()) { + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_pointer) + << DCPT->getKind() << DCPT->isArrayType() << RHSExp->getSourceRange(); + return false; + } else { + const auto *DRPTy = Ty->getAs(); + assert(DRPTy); + if (!DRPTy->getEndPointer()) + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << /*ended_by*/ 4 << isa(VD) << RHSExp->getSourceRange(); + else + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_pointer) + << /*ended_by*/ 4 << /*pointer*/ 0 << RHSExp->getSourceRange(); + return false; + } + } + + return checkDynamicBoundVariableEscapeInRetType(*this, RHSExp, VD, + ExpAddrOfLevel); } -/// Return the resulting type when a vector is shifted -/// by a scalar or vector shift amount. -static QualType checkVectorShift(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompAssign) { - // OpenCL v1.1 s6.3.j says RHS can be a vector only if LHS is a vector. - if ((S.LangOpts.OpenCL || S.LangOpts.ZVector) && - !LHS.get()->getType()->isVectorType()) { - S.Diag(Loc, diag::err_shift_rhs_only_vector) - << RHS.get()->getType() << LHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); +Sema::AssignConvertType +Sema::CheckValueTerminatedAssignmentConstraints(QualType LHSType, + Expr *RHSExpr) { + const auto *LVTT = LHSType->getAs(); + if (LVTT && LVTT->isPointerType()) { + const auto *SL = dyn_cast(RHSExpr->IgnoreParenImpCasts()); + if (!SL) { + if (const auto *PDE = + dyn_cast(RHSExpr->IgnoreParenImpCasts())) { + SL = PDE->getFunctionName(); + } + } + if (SL) { + QualType CharT = Context.getAsArrayType(SL->getType())->getElementType(); + if (Context.hasSameUnqualifiedType(LVTT->getPointeeType(), CharT)) { + return LVTT->getTerminatorValue(Context).isZero() + ? Sema::Compatible + : Sema::IncompatibleStringLiteralToValueTerminatedPointer; + } + } } - if (!IsCompAssign) { - LHS = S.UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) return QualType(); + QualType L = LHSType; + QualType R = RHSExpr->getType(); + bool Nested = false; + while (L->isPointerType() && R->isPointerType()) { + QualType LPointee = L->getPointeeType(); + QualType RPointee = R->getPointeeType(); + const auto *LVTT = L->getAs(); + const auto *RVTT = R->getAs(); + if (LVTT && RVTT) { + if (LVTT != RVTT && + !llvm::APSInt::isSameValue(LVTT->getTerminatorValue(Context), + RVTT->getTerminatorValue(Context))) { + return Sema::IncompatibleValueTerminatedTerminators; + } + } else if (!LVTT && RVTT && L->isSafePointerType()) { + return Nested + ? Sema:: + IncompatibleNestedValueTerminatedToNonValueTerminatedPointer + : Sema::IncompatibleValueTerminatedToNonValueTerminatedPointer; + } else if (LVTT && !RVTT) { + // If the expression is a constant expression, try to redeem it by + // checking if it is terminated with the right value. + // Don't try to evaluate the terminator for nested pointers, since + // RHSExpr matches the assignment only for the first level pointers (we + // don't have an expression for nested pointers). + // Ensure that the pointees are compatible (including sugar types), + // since we return Sema::Compatible here immediately without iterating + // nested levels. + if (!Nested) { + Expr::EvalResult Evald; + bool CompatiblePointees = + (Context.hasSameUnqualifiedType(LPointee, RPointee) && + Context.canMergeInnerTypeBounds(LPointee, RPointee)) || + (LPointee->isIntegerType() && RPointee->isIntegerType() && + Context.hasSameUnqualifiedType( + Context.getCorrespondingSignedType(LPointee), + Context.getCorrespondingSignedType(RPointee))); + if (CompatiblePointees && + RHSExpr->tryEvaluateTerminatorElement(Evald, Context) && + Evald.Val.isInt() && + llvm::APSInt::isSameValue(LVTT->getTerminatorValue(Context), + Evald.Val.getInt())) { + return Sema::Compatible; + } + } + return Nested + ? Sema:: + IncompatibleNestedNonValueTerminatedToValueTerminatedPointer + : Sema:: + IncompatibleNonValueTerminatedToValueTerminatedPointer; + } + L = LPointee; + R = RPointee; + Nested = true; } - RHS = S.UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) return QualType(); - - QualType LHSType = LHS.get()->getType(); - // Note that LHS might be a scalar because the routine calls not only in - // OpenCL case. - const VectorType *LHSVecTy = LHSType->getAs(); - QualType LHSEleType = LHSVecTy ? LHSVecTy->getElementType() : LHSType; + return Sema::Compatible; +} - // Note that RHS might not be a vector. - QualType RHSType = RHS.get()->getType(); - const VectorType *RHSVecTy = RHSType->getAs(); - QualType RHSEleType = RHSVecTy ? RHSVecTy->getElementType() : RHSType; +bool Sema::isCompatibleBoundsUnsafeAssignment( + Sema::AssignConvertType ConvTy) const { + // This switch intentionally has no default case so that it emits + // a warning if a new AssignConvertType is added without being checked here. + switch (ConvTy) { + case IncompatibleIntToSafePointer: + case IncompatibleStringLiteralToValueTerminatedPointer: + case IncompatibleValueTerminatedTerminators: + case IncompatibleValueTerminatedToNonValueTerminatedPointer: + case IncompatibleNonValueTerminatedToValueTerminatedPointer: + case IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + case IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + case IncompatibleBoundsSafetyFunctionPointer: + case IncompatibleUnsafeToSafePointer: + return true; - // Do not allow shifts for boolean vectors. - if ((LHSVecTy && LHSVecTy->isExtVectorBoolType()) || - (RHSVecTy && RHSVecTy->isExtVectorBoolType())) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange(); - return QualType(); + // These -fbounds-safety errors shouldn't be ignored because of ABI mismatch. + // Codegen support has not been tested. + case IncompatibleUnsafeToIndexablePointer: + case IncompleteSingleToIndexablePointer: + case IncompatibleNestedBoundsSafetyPointerAttributes: + // Non bounds-safety errors + case IncompatibleVectors: + case IntToBlockPointer: + case IncompatibleBlockPointer: + case IncompatibleObjCQualifiedId: + case IncompatibleObjCWeakRef: + case FunctionVoidPointer: + case IncompatiblePointer: + case IncompatibleFunctionPointer: + case IncompatibleFunctionPointerStrict: + case IncompatiblePointerSign: + case CompatiblePointerDiscardsQualifiers: + case IncompatiblePointerDiscardsQualifiers: + case IncompatibleNestedPointerAddressSpaceMismatch: + case IncompatibleNestedPointerQualifiers: + case PointerToInt: + case IntToPointer: + case Incompatible: + // Not errors + case CompatibleSingleToExplicitIndexablePointer: + case Compatible: + return false; } + llvm_unreachable("Unhandled Sema::AssignConvertType"); +} - // The operands need to be integers. - if (!LHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << LHS.get()->getType() << LHS.get()->getSourceRange(); - return QualType(); - } +bool Sema::allowBoundsUnsafeFunctionArg(const CallExpr *CallE, + unsigned ParamIdx) const { + const auto *Callee = CallE->getCallee(); - if (!RHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << RHS.get()->getType() << RHS.get()->getSourceRange(); - return QualType(); - } + QualType CalleeTy = Callee->getType(); + if (CalleeTy->isFunctionPointerType() || CalleeTy->isFunctionReferenceType()) + CalleeTy = CalleeTy->getPointeeType(); - if (!LHSVecTy) { - assert(RHSVecTy); - if (IsCompAssign) - return RHSType; - if (LHSEleType != RHSEleType) { - LHS = S.ImpCastExprToType(LHS.get(),RHSEleType, CK_IntegralCast); - LHSEleType = RHSEleType; - } - QualType VecTy = - S.Context.getExtVectorType(LHSEleType, RHSVecTy->getNumElements()); - LHS = S.ImpCastExprToType(LHS.get(), VecTy, CK_VectorSplat); - LHSType = VecTy; - } else if (RHSVecTy) { - // OpenCL v1.1 s6.3.j says that for vector types, the operators - // are applied component-wise. So if RHS is a vector, then ensure - // that the number of elements is the same as LHS... - if (RHSVecTy->getNumElements() != LHSVecTy->getNumElements()) { - S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } - if (!S.LangOpts.OpenCL && !S.LangOpts.ZVector) { - const BuiltinType *LHSBT = LHSEleType->getAs(); - const BuiltinType *RHSBT = RHSEleType->getAs(); - if (LHSBT != RHSBT && - S.Context.getTypeSize(LHSBT) != S.Context.getTypeSize(RHSBT)) { - S.Diag(Loc, diag::warn_typecheck_vector_element_sizes_not_equal) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - } - } - } else { - // ...else expand RHS to match the number of elements in LHS. - QualType VecTy = - S.Context.getExtVectorType(RHSEleType, LHSVecTy->getNumElements()); - RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); - } + const auto *FuncTy = CalleeTy->getAs(); + if (!FuncTy) + return false; - return LHSType; + return allowBoundsUnsafePointerAssignment(FuncTy->getParamType(ParamIdx), + CallE->getArg(ParamIdx), + CallE->getExprLoc()); } -static QualType checkSizelessVectorShift(Sema &S, ExprResult &LHS, - ExprResult &RHS, SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = S.UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - - RHS = S.UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) - return QualType(); +bool Sema::allowBoundsUnsafePointerAssignment( + const QualType DestTy, const Expr *SourceValue, + SourceLocation AssignmentLoc) const { - QualType LHSType = LHS.get()->getType(); - const BuiltinType *LHSBuiltinTy = LHSType->castAs(); - QualType LHSEleType = LHSType->isSveVLSBuiltinType() - ? LHSBuiltinTy->getSveEltType(S.getASTContext()) - : LHSType; + SourceValue = SourceValue->IgnoreParenImpCasts(); - // Note that RHS might not be a vector - QualType RHSType = RHS.get()->getType(); - const BuiltinType *RHSBuiltinTy = RHSType->castAs(); - QualType RHSEleType = RHSType->isSveVLSBuiltinType() - ? RHSBuiltinTy->getSveEltType(S.getASTContext()) - : RHSType; + // Integer to pointer conversions are ABI compatible, but make no exception + // to allow them for now. This may change later if a use case is found. + auto DestPtrTy = DestTy->getAs(); + if (!DestPtrTy) + return false; + auto SourcePtrTy = SourceValue->getType()->getAs(); + if (!SourcePtrTy) + return false; - if ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || - (RHSBuiltinTy && RHSBuiltinTy->isSVEBool())) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHSType << RHSType << LHS.get()->getSourceRange(); - return QualType(); - } + bool DestSafe = DestPtrTy->isSafePointer(); + bool SourceSafe = SourcePtrTy->isSafePointer(); + // Assignments between unsafe pointers will be allowed regardless, no need to + // make an exception. + if (!DestSafe && !SourceSafe) + return false; - if (!LHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << LHS.get()->getType() << LHS.get()->getSourceRange(); - return QualType(); - } + // All safe pointers ABI compatible with unsafe pointers (and each other) are + // __single pointers. Make no exception for ABI incompatible pointer type + // mismatch. + if (!DestPtrTy->isSingle() && DestSafe) + return false; + if (!SourcePtrTy->isSingle() && SourceSafe) + return false; - if (!RHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << RHS.get()->getType() << RHS.get()->getSourceRange(); - return QualType(); - } + return allowBoundsUnsafeAssignment(AssignmentLoc); +} - if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && - (S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != - S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC)) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } +bool Sema::allowBoundsUnsafeAssignment(SourceLocation AssignmentLoc) const { + // System headers are excepted from respecting bounds safety rules + // since they are external to the main project (and outside of its control) + // and may not have adopted -fbounds-safety even when the main project and/or + // transitive dependencies have done so. Code in system headers without + // -fbounds-safety adoption is compiled as if -fbounds-safety was disabled. + if (!SourceMgr.isInSystemHeader(AssignmentLoc)) + return false; - if (!LHSType->isSveVLSBuiltinType()) { - assert(RHSType->isSveVLSBuiltinType()); - if (IsCompAssign) - return RHSType; - if (LHSEleType != RHSEleType) { - LHS = S.ImpCastExprToType(LHS.get(), RHSEleType, clang::CK_IntegralCast); - LHSEleType = RHSEleType; - } - const llvm::ElementCount VecSize = - S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC; - QualType VecTy = - S.Context.getScalableVectorType(LHSEleType, VecSize.getKnownMinValue()); - LHS = S.ImpCastExprToType(LHS.get(), VecTy, clang::CK_VectorSplat); - LHSType = VecTy; - } else if (RHSBuiltinTy && RHSBuiltinTy->isSveVLSBuiltinType()) { - if (S.Context.getTypeSize(RHSBuiltinTy) != - S.Context.getTypeSize(LHSBuiltinTy)) { - S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } - } else { - const llvm::ElementCount VecSize = - S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC; - if (LHSEleType != RHSEleType) { - RHS = S.ImpCastExprToType(RHS.get(), LHSEleType, clang::CK_IntegralCast); - RHSEleType = LHSEleType; - } - QualType VecTy = - S.Context.getScalableVectorType(RHSEleType, VecSize.getKnownMinValue()); - RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); - } + // If the default pointer ABI in the header is not unsafe_indexable or + // unspecified the header has adopted -fbounds-safety and making exceptions to the + // safety rules would be counter-productive. + if (!CurPointerAbi.isUnsafeOrUnspecified()) + return false; - return LHSType; + return getLangOpts().BoundsSafetyRelaxedSystemHeaders; } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -// C99 6.5.7 -QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - bool IsCompAssign) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); +Sema::AssignConvertType +Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, + bool Diagnose, + bool DiagnoseCFAudited, + bool ConvertRHS) { + // We need to be able to tell the caller whether we diagnosed a problem, if + // they ask us to issue diagnostics. + assert((ConvertRHS || !Diagnose) && "can't indicate whether we diagnosed"); - // Vector shifts promote their scalar inputs to vector type. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - if (LangOpts.ZVector) { - // The shift operators for the z vector extensions work basically - // like general shifts, except that neither the LHS nor the RHS is - // allowed to be a "vector bool". - if (auto LHSVecType = LHS.get()->getType()->getAs()) - if (LHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return InvalidOperands(Loc, LHS, RHS); - if (auto RHSVecType = RHS.get()->getType()->getAs()) - if (RHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return InvalidOperands(Loc, LHS, RHS); + // If ConvertRHS is false, we want to leave the caller's RHS untouched. Sadly, + // we can't avoid *all* modifications at the moment, so we need some somewhere + // to put the updated value. + ExprResult LocalRHS = CallerRHS; + ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; + + if (const auto *LHSPtrType = LHSType->getAs()) { + if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { + if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + Diag(RHS.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << RHS.get()->getSourceRange(); + } } - return checkVectorShift(*this, LHS, RHS, Loc, IsCompAssign); } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) - return checkSizelessVectorShift(*this, LHS, RHS, Loc, IsCompAssign); - - // Shifts don't perform usual arithmetic conversions, they just do integer - // promotions on each operand. C99 6.5.7p3 - - // For the LHS, do usual unary conversions, but then reset them away - // if this is a compound assignment. - ExprResult OldLHS = LHS; - LHS = UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - QualType LHSType = LHS.get()->getType(); - if (IsCompAssign) LHS = OldLHS; - - // The RHS is simpler. - RHS = UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - QualType RHSType = RHS.get()->getType(); + if (getLangOpts().CPlusPlus) { + if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { + // C++ 5.17p3: If the left operand is not of class type, the + // expression is implicitly converted (C++ 4) to the + // cv-unqualified type of the left operand. + QualType RHSType = RHS.get()->getType(); + if (Diagnose) { + RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + AA_Assigning); + } else { + ImplicitConversionSequence ICS = + TryImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + /*SuppressUserConversions=*/false, + AllowedExplicit::None, + /*InOverloadResolution=*/false, + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); + if (ICS.isFailure()) + return Incompatible; + RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + ICS, AA_Assigning); + } + if (RHS.isInvalid()) + return Incompatible; + Sema::AssignConvertType result = Compatible; + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + !ObjC().CheckObjCARCUnavailableWeakConversion(LHSType, RHSType)) + result = IncompatibleObjCWeakRef; + return result; + } - // C99 6.5.7p2: Each of the operands shall have integer type. - // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point. - if ((!LHSType->isFixedPointOrIntegerType() && - !LHSType->hasIntegerRepresentation()) || - !RHSType->hasIntegerRepresentation()) - return InvalidOperands(Loc, LHS, RHS); + // FIXME: Currently, we fall through and treat C++ classes like C + // structures. + // FIXME: We also fall through for atomics; not sure what should + // happen there, though. + } else if (RHS.get()->getType() == Context.OverloadTy) { + // As a set of extensions to C, we support overloading on functions. These + // functions need to be resolved here. + DeclAccessPair DAP; + if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction( + RHS.get(), LHSType, /*Complain=*/false, DAP)) + RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD); + else + return Incompatible; + } - // C++0x: Don't allow scoped enums. FIXME: Use something better than - // hasIntegerRepresentation() above instead of this. - if (isScopedEnumerationType(LHSType) || - isScopedEnumerationType(RHSType)) { - return InvalidOperands(Loc, LHS, RHS); + // This check seems unnatural, however it is necessary to ensure the proper + // conversion of functions/arrays. If the conversion were done for all + // DeclExpr's (created by ActOnIdExpression), it would mess up the unary + // expressions that suppress this implicit conversion (&, sizeof). This needs + // to happen before we check for null pointer conversions because C does not + // undergo the same implicit conversions as C++ does above (by the calls to + // TryImplicitConversion() and PerformImplicitConversion()) which insert the + // lvalue to rvalue cast before checking for null pointer constraints. This + // addresses code like: nullptr_t val; int *ptr; ptr = val; + // + // Suppress this for references: C++ 8.5.3p5. + if (!LHSType->isReferenceType()) { + // FIXME: We potentially allocate here even if ConvertRHS is false. + RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); + if (RHS.isInvalid()) + return Incompatible; } - DiagnoseBadShiftValues(*this, LHS, RHS, Loc, Opc, LHSType); - // "The type of the result is that of the promoted left operand." - return LHSType; -} - -/// Diagnose bad pointer comparisons. -static void diagnoseDistinctPointerComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS, - bool IsError) { - S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_distinct_pointers - : diag::ext_typecheck_comparison_of_distinct_pointers) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); -} - -/// Returns false if the pointers are converted to a composite type, -/// true otherwise. -static bool convertPointersToCompositeType(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS) { - // C++ [expr.rel]p2: - // [...] Pointer conversions (4.10) and qualification - // conversions (4.4) are performed on pointer operands (or on - // a pointer operand and a null pointer constant) to bring - // them to their composite pointer type. [...] - // - // C++ [expr.eq]p1 uses the same notion for (in)equality - // comparisons of pointers. - - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - assert(LHSType->isPointerType() || RHSType->isPointerType() || - LHSType->isMemberPointerType() || RHSType->isMemberPointerType()); + // The constraints are expressed in terms of the atomic, qualified, or + // unqualified type of the LHS. + QualType LHSTypeAfterConversion = LHSType.getAtomicUnqualifiedType(); - QualType T = S.FindCompositePointerType(Loc, LHS, RHS); - if (T.isNull()) { - if ((LHSType->isAnyPointerType() || LHSType->isMemberPointerType()) && - (RHSType->isAnyPointerType() || RHSType->isMemberPointerType())) - diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); - else - S.InvalidOperands(Loc, LHS, RHS); - return true; + // C99 6.5.16.1p1: the left operand is a pointer and the right is + // a null pointer constant or its type is nullptr_t;. + if ((LHSTypeAfterConversion->isPointerType() || + LHSTypeAfterConversion->isObjCObjectPointerType() || + LHSTypeAfterConversion->isBlockPointerType()) && + ((getLangOpts().C23 && RHS.get()->getType()->isNullPtrType()) || + RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull))) { + if (Diagnose || ConvertRHS) { + CastKind Kind; + CXXCastPath Path; + CheckPointerConversion(RHS.get(), LHSType, Kind, Path, + /*IgnoreBaseAccess=*/false, Diagnose); + if (ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_PRValue, &Path); + } + return Compatible; + } + // C23 6.5.16.1p1: the left operand has type atomic, qualified, or + // unqualified bool, and the right operand is a pointer or its type is + // nullptr_t. + if (getLangOpts().C23 && LHSType->isBooleanType() && + RHS.get()->getType()->isNullPtrType()) { + // NB: T* -> _Bool is handled in CheckAssignmentConstraints, this only + // only handles nullptr -> _Bool due to needing an extra conversion + // step. + // We model this by converting from nullptr -> void * and then let the + // conversion from void * -> _Bool happen naturally. + if (Diagnose || ConvertRHS) { + CastKind Kind; + CXXCastPath Path; + CheckPointerConversion(RHS.get(), Context.VoidPtrTy, Kind, Path, + /*IgnoreBaseAccess=*/false, Diagnose); + if (ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), Context.VoidPtrTy, Kind, VK_PRValue, + &Path); + } } - return false; -} - -static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, - ExprResult &RHS, - bool IsError) { - S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_fptr_to_void - : diag::ext_typecheck_comparison_of_fptr_to_void) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); -} + // OpenCL queue_t type assignment. + if (LHSType->isQueueT() && RHS.get()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNull)) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return Compatible; + } -static bool isObjCObjectLiteral(ExprResult &E) { - switch (E.get()->IgnoreParenImpCasts()->getStmtClass()) { - case Stmt::ObjCArrayLiteralClass: - case Stmt::ObjCDictionaryLiteralClass: - case Stmt::ObjCStringLiteralClass: - case Stmt::ObjCBoxedExprClass: - return true; - default: - // Note that ObjCBoolLiteral is NOT an object literal! - return false; + Expr *PRE = RHS.get()->IgnoreParenCasts(); + if (Diagnose && isa(PRE)) { + ObjCProtocolDecl *PDecl = cast(PRE)->getProtocol(); + if (PDecl && !PDecl->hasDefinition()) { + Diag(PRE->getExprLoc(), diag::warn_atprotocol_protocol) << PDecl; + Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; + } } -} -static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) { - const ObjCObjectPointerType *Type = - LHS->getType()->getAs(); + Sema::AssignConvertType result = Compatible; + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + result = CheckValueTerminatedAssignmentConstraints(LHSType, RHS.get()); - // If this is not actually an Objective-C object, bail out. - if (!Type) - return false; + CastKind Kind = CK_NoOp; + if (result == Compatible) + /*TO_UPSTREAM(BoundsSafety) OFF*/ + result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); - // Get the LHS object's interface type. - QualType InterfaceType = Type->getPointeeType(); + // C99 6.5.16.1p2: The value of the right operand is converted to the + // type of the assignment expression. + // CheckAssignmentConstraints allows the left-hand side to be a reference, + // so that we can use references in built-in functions even in C. + // The getNonReferenceType() call makes sure that the resulting expression + // does not have reference type. + if (result != Incompatible && RHS.get()->getType() != LHSType) { + QualType Ty = LHSType.getNonLValueExprType(Context); + Expr *E = RHS.get(); - // If the RHS isn't an Objective-C object, bail out. - if (!RHS->getType()->isObjCObjectPointerType()) - return false; + // Check for various Objective-C errors. If we are not reporting + // diagnostics and just checking for errors, e.g., during overload + // resolution, return Incompatible to indicate the failure. + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + ObjC().CheckObjCConversion(SourceRange(), Ty, E, + CheckedConversionKind::Implicit, Diagnose, + DiagnoseCFAudited) != SemaObjC::ACR_okay) { + if (!Diagnose) + return Incompatible; + } + if (getLangOpts().ObjC && + (ObjC().CheckObjCBridgeRelatedConversions(E->getBeginLoc(), LHSType, + E->getType(), E, Diagnose) || + ObjC().CheckConversionToObjCLiteral(LHSType, E, Diagnose))) { + if (!Diagnose) + return Incompatible; + // Replace the expression with a corrected version and continue so we + // can find further errors. + RHS = E; + return Compatible; + } - // Try to find the -isEqual: method. - Selector IsEqualSel = S.ObjC().NSAPIObj->getIsEqualSelector(); - ObjCMethodDecl *Method = - S.ObjC().LookupMethodInObjectType(IsEqualSel, InterfaceType, - /*IsInstance=*/true); - if (!Method) { - if (Type->isObjCIdType()) { - // For 'id', just check the global pool. - Method = - S.ObjC().LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(), - /*receiverId=*/true); - } else { - // Check protocols. - Method = S.ObjC().LookupMethodInQualifiedType(IsEqualSel, Type, - /*IsInstance=*/true); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety && + isCompatibleBoundsUnsafeAssignment(result)) { + // Leave the AST intact and let the caller decide how to handle the error + if (ConvertRHS && !E->getType()->isPointerType()) + E = ImpCastExprToType(E, QualType(Ty->getUnqualifiedDesugaredType(), 0), + CK_IntegralToPointer) + .get(); + Kind = CK_BoundsSafetyPointerCast; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + if (ConvertRHS) + RHS = ImpCastExprToType(E, Ty, Kind); } - if (!Method) - return false; + return result; +} - QualType T = Method->parameters()[0]->getType(); - if (!T->isObjCObjectPointerType()) - return false; +namespace { +/// The original operand to an operator, prior to the application of the usual +/// arithmetic conversions and converting the arguments of a builtin operator +/// candidate. +struct OriginalOperand { + explicit OriginalOperand(Expr *Op) : Orig(Op), Conversion(nullptr) { + if (auto *MTE = dyn_cast(Op)) + Op = MTE->getSubExpr(); + if (auto *BTE = dyn_cast(Op)) + Op = BTE->getSubExpr(); + if (auto *ICE = dyn_cast(Op)) { + Orig = ICE->getSubExprAsWritten(); + Conversion = ICE->getConversionFunction(); + } + } - QualType R = Method->getReturnType(); - if (!R->isScalarType()) - return false; + QualType getType() const { return Orig->getType(); } - return true; + Expr *Orig; + NamedDecl *Conversion; +}; } -static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS, - BinaryOperator::Opcode Opc){ - Expr *Literal; - Expr *Other; - if (isObjCObjectLiteral(LHS)) { - Literal = LHS.get(); - Other = RHS.get(); - } else { - Literal = RHS.get(); - Other = LHS.get(); - } +QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &LHS, + ExprResult &RHS) { + OriginalOperand OrigLHS(LHS.get()), OrigRHS(RHS.get()); - // Don't warn on comparisons against nil. - Other = Other->IgnoreParenCasts(); - if (Other->isNullPointerConstant(S.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) - return; + Diag(Loc, diag::err_typecheck_invalid_operands) + << OrigLHS.getType() << OrigRHS.getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - // This should be kept in sync with warn_objc_literal_comparison. - // LK_String should always be after the other literals, since it has its own - // warning flag. - SemaObjC::ObjCLiteralKind LiteralKind = S.ObjC().CheckLiteralKind(Literal); - assert(LiteralKind != SemaObjC::LK_Block); - if (LiteralKind == SemaObjC::LK_None) { - llvm_unreachable("Unknown Objective-C object literal kind"); + // If a user-defined conversion was applied to either of the operands prior + // to applying the built-in operator rules, tell the user about it. + if (OrigLHS.Conversion) { + Diag(OrigLHS.Conversion->getLocation(), + diag::note_typecheck_invalid_operands_converted) + << 0 << LHS.get()->getType(); + } + if (OrigRHS.Conversion) { + Diag(OrigRHS.Conversion->getLocation(), + diag::note_typecheck_invalid_operands_converted) + << 1 << RHS.get()->getType(); } - if (LiteralKind == SemaObjC::LK_String) - S.Diag(Loc, diag::warn_objc_string_literal_comparison) - << Literal->getSourceRange(); - else - S.Diag(Loc, diag::warn_objc_literal_comparison) - << LiteralKind << Literal->getSourceRange(); + return QualType(); +} - if (BinaryOperator::isEqualityOp(Opc) && - hasIsEqualMethod(S, LHS.get(), RHS.get())) { - SourceLocation Start = LHS.get()->getBeginLoc(); - SourceLocation End = S.getLocForEndOfToken(RHS.get()->getEndLoc()); - CharSourceRange OpRange = - CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); - - S.Diag(Loc, diag::note_objc_literal_comparison_isequal) - << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![") - << FixItHint::CreateReplacement(OpRange, " isEqual:") - << FixItHint::CreateInsertion(End, "]"); - } -} +QualType Sema::InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS, + ExprResult &RHS) { + QualType LHSType = LHS.get()->IgnoreImpCasts()->getType(); + QualType RHSType = RHS.get()->IgnoreImpCasts()->getType(); -/// Warns on !x < y, !x & y where !(x < y), !(x & y) was probably intended. -static void diagnoseLogicalNotOnLHSofCheck(Sema &S, ExprResult &LHS, - ExprResult &RHS, SourceLocation Loc, - BinaryOperatorKind Opc) { - // Check that left hand side is !something. - UnaryOperator *UO = dyn_cast(LHS.get()->IgnoreImpCasts()); - if (!UO || UO->getOpcode() != UO_LNot) return; + bool LHSNatVec = LHSType->isVectorType(); + bool RHSNatVec = RHSType->isVectorType(); - // Only check if the right hand side is non-bool arithmetic type. - if (RHS.get()->isKnownToHaveBooleanValue()) return; + if (!(LHSNatVec && RHSNatVec)) { + Expr *Vector = LHSNatVec ? LHS.get() : RHS.get(); + Expr *NonVector = !LHSNatVec ? LHS.get() : RHS.get(); + Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) + << 0 << Vector->getType() << NonVector->IgnoreImpCasts()->getType() + << Vector->getSourceRange(); + return QualType(); + } - // Make sure that the something in !something is not bool. - Expr *SubExpr = UO->getSubExpr()->IgnoreImpCasts(); - if (SubExpr->isKnownToHaveBooleanValue()) return; + Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) + << 1 << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); - // Emit warning. - bool IsBitwiseOp = Opc == BO_And || Opc == BO_Or || Opc == BO_Xor; - S.Diag(UO->getOperatorLoc(), diag::warn_logical_not_on_lhs_of_check) - << Loc << IsBitwiseOp; + return QualType(); +} - // First note suggest !(x < y) - SourceLocation FirstOpen = SubExpr->getBeginLoc(); - SourceLocation FirstClose = RHS.get()->getEndLoc(); - FirstClose = S.getLocForEndOfToken(FirstClose); - if (FirstClose.isInvalid()) - FirstOpen = SourceLocation(); - S.Diag(UO->getOperatorLoc(), diag::note_logical_not_fix) - << IsBitwiseOp - << FixItHint::CreateInsertion(FirstOpen, "(") - << FixItHint::CreateInsertion(FirstClose, ")"); +/// Try to convert a value of non-vector type to a vector type by converting +/// the type to the element type of the vector and then performing a splat. +/// If the language is OpenCL, we only use conversions that promote scalar +/// rank; for C, Obj-C, and C++ we allow any real scalar conversion except +/// for float->int. +/// +/// OpenCL V2.0 6.2.6.p2: +/// An error shall occur if any scalar operand type has greater rank +/// than the type of the vector element. +/// +/// \param scalar - if non-null, actually perform the conversions +/// \return true if the operation fails (but without diagnosing the failure) +static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar, + QualType scalarTy, + QualType vectorEltTy, + QualType vectorTy, + unsigned &DiagID) { + // The conversion to apply to the scalar before splatting it, + // if necessary. + CastKind scalarCast = CK_NoOp; - // Second note suggests (!x) < y - SourceLocation SecondOpen = LHS.get()->getBeginLoc(); - SourceLocation SecondClose = LHS.get()->getEndLoc(); - SecondClose = S.getLocForEndOfToken(SecondClose); - if (SecondClose.isInvalid()) - SecondOpen = SourceLocation(); - S.Diag(UO->getOperatorLoc(), diag::note_logical_not_silence_with_parens) - << FixItHint::CreateInsertion(SecondOpen, "(") - << FixItHint::CreateInsertion(SecondClose, ")"); -} + if (vectorEltTy->isIntegralType(S.Context)) { + if (S.getLangOpts().OpenCL && (scalarTy->isRealFloatingType() || + (scalarTy->isIntegerType() && + S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0))) { + DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; + return true; + } + if (!scalarTy->isIntegralType(S.Context)) + return true; + scalarCast = CK_IntegralCast; + } else if (vectorEltTy->isRealFloatingType()) { + if (scalarTy->isRealFloatingType()) { + if (S.getLangOpts().OpenCL && + S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) { + DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; + return true; + } + scalarCast = CK_FloatingCast; + } + else if (scalarTy->isIntegralType(S.Context)) + scalarCast = CK_IntegralToFloating; + else + return true; + } else { + return true; + } -// Returns true if E refers to a non-weak array. -static bool checkForArray(const Expr *E) { - const ValueDecl *D = nullptr; - if (const DeclRefExpr *DR = dyn_cast(E)) { - D = DR->getDecl(); - } else if (const MemberExpr *Mem = dyn_cast(E)) { - if (Mem->isImplicitAccess()) - D = Mem->getMemberDecl(); + // Adjust scalar if desired. + if (scalar) { + if (scalarCast != CK_NoOp) + *scalar = S.ImpCastExprToType(scalar->get(), vectorEltTy, scalarCast); + *scalar = S.ImpCastExprToType(scalar->get(), vectorTy, CK_VectorSplat); } - if (!D) - return false; - return D->getType()->isArrayType() && !D->isWeak(); + return false; } -/// Diagnose some forms of syntactically-obvious tautological comparison. -static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, - Expr *LHS, Expr *RHS, - BinaryOperatorKind Opc) { - Expr *LHSStripped = LHS->IgnoreParenImpCasts(); - Expr *RHSStripped = RHS->IgnoreParenImpCasts(); +/// Convert vector E to a vector with the same number of elements but different +/// element type. +static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) { + const auto *VecTy = E->getType()->getAs(); + assert(VecTy && "Expression E must be a vector"); + QualType NewVecTy = + VecTy->isExtVectorType() + ? S.Context.getExtVectorType(ElementType, VecTy->getNumElements()) + : S.Context.getVectorType(ElementType, VecTy->getNumElements(), + VecTy->getVectorKind()); - QualType LHSType = LHS->getType(); - QualType RHSType = RHS->getType(); - if (LHSType->hasFloatingRepresentation() || - (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || - S.inTemplateInstantiation()) - return; + // Look through the implicit cast. Return the subexpression if its type is + // NewVecTy. + if (auto *ICE = dyn_cast(E)) + if (ICE->getSubExpr()->getType() == NewVecTy) + return ICE->getSubExpr(); - // WebAssembly Tables cannot be compared, therefore shouldn't emit - // Tautological diagnostics. - if (LHSType->isWebAssemblyTableType() || RHSType->isWebAssemblyTableType()) - return; + auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast; + return S.ImpCastExprToType(E, NewVecTy, Cast); +} - // Comparisons between two array types are ill-formed for operator<=>, so - // we shouldn't emit any additional warnings about it. - if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) - return; +/// Test if a (constant) integer Int can be casted to another integer type +/// IntTy without losing precision. +static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int, + QualType OtherIntTy) { + QualType IntTy = Int->get()->getType().getUnqualifiedType(); - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - // - // NOTE: Don't warn about comparison expressions resulting from macro - // expansion. Also don't warn about comparisons which are only self - // comparisons within a template instantiation. The warnings should catch - // obvious cases in the definition of the template anyways. The idea is to - // warn when the typed comparison operator will always evaluate to the same - // result. + // Reject cases where the value of the Int is unknown as that would + // possibly cause truncation, but accept cases where the scalar can be + // demoted without loss of precision. + Expr::EvalResult EVResult; + bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); + int Order = S.Context.getIntegerTypeOrder(OtherIntTy, IntTy); + bool IntSigned = IntTy->hasSignedIntegerRepresentation(); + bool OtherIntSigned = OtherIntTy->hasSignedIntegerRepresentation(); - // Used for indexing into %select in warn_comparison_always - enum { - AlwaysConstant, - AlwaysTrue, - AlwaysFalse, - AlwaysEqual, // std::strong_ordering::equal from operator<=> - }; + if (CstInt) { + // If the scalar is constant and is of a higher order and has more active + // bits that the vector element type, reject it. + llvm::APSInt Result = EVResult.Val.getInt(); + unsigned NumBits = IntSigned + ? (Result.isNegative() ? Result.getSignificantBits() + : Result.getActiveBits()) + : Result.getActiveBits(); + if (Order < 0 && S.Context.getIntWidth(OtherIntTy) < NumBits) + return true; - // C++2a [depr.array.comp]: - // Equality and relational comparisons ([expr.eq], [expr.rel]) between two - // operands of array type are deprecated. - if (S.getLangOpts().CPlusPlus20 && LHSStripped->getType()->isArrayType() && - RHSStripped->getType()->isArrayType()) { - S.Diag(Loc, diag::warn_depr_array_comparison) - << LHS->getSourceRange() << RHS->getSourceRange() - << LHSStripped->getType() << RHSStripped->getType(); - // Carry on to produce the tautological comparison warning, if this - // expression is potentially-evaluated, we can resolve the array to a - // non-weak declaration, and so on. + // If the signedness of the scalar type and the vector element type + // differs and the number of bits is greater than that of the vector + // element reject it. + return (IntSigned != OtherIntSigned && + NumBits > S.Context.getIntWidth(OtherIntTy)); } - if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) { - if (Expr::isSameComparisonOperand(LHS, RHS)) { - unsigned Result; - switch (Opc) { - case BO_EQ: - case BO_LE: - case BO_GE: - Result = AlwaysTrue; - break; - case BO_NE: - case BO_LT: - case BO_GT: - Result = AlwaysFalse; - break; - case BO_Cmp: - Result = AlwaysEqual; - break; - default: - Result = AlwaysConstant; - break; - } - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_comparison_always) - << 0 /*self-comparison*/ - << Result); - } else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) { - // What is it always going to evaluate to? - unsigned Result; - switch (Opc) { - case BO_EQ: // e.g. array1 == array2 - Result = AlwaysFalse; - break; - case BO_NE: // e.g. array1 != array2 - Result = AlwaysTrue; - break; - default: // e.g. array1 <= array2 - // The best we can say is 'a constant' - Result = AlwaysConstant; - break; - } - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_comparison_always) - << 1 /*array comparison*/ - << Result); - } - } + // Reject cases where the value of the scalar is not constant and it's + // order is greater than that of the vector element type. + return (Order < 0); +} - if (isa(LHSStripped)) - LHSStripped = LHSStripped->IgnoreParenCasts(); - if (isa(RHSStripped)) - RHSStripped = RHSStripped->IgnoreParenCasts(); +/// Test if a (constant) integer Int can be casted to floating point type +/// FloatTy without losing precision. +static bool canConvertIntTyToFloatTy(Sema &S, ExprResult *Int, + QualType FloatTy) { + QualType IntTy = Int->get()->getType().getUnqualifiedType(); - // Warn about comparisons against a string constant (unless the other - // operand is null); the user probably wants string comparison function. - Expr *LiteralString = nullptr; - Expr *LiteralStringStripped = nullptr; - if ((isa(LHSStripped) || isa(LHSStripped)) && - !RHSStripped->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) { - LiteralString = LHS; - LiteralStringStripped = LHSStripped; - } else if ((isa(RHSStripped) || - isa(RHSStripped)) && - !LHSStripped->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) { - LiteralString = RHS; - LiteralStringStripped = RHSStripped; - } + // Determine if the integer constant can be expressed as a floating point + // number of the appropriate type. + Expr::EvalResult EVResult; + bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); - if (LiteralString) { - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_stringcompare) - << isa(LiteralStringStripped) - << LiteralString->getSourceRange()); + uint64_t Bits = 0; + if (CstInt) { + // Reject constants that would be truncated if they were converted to + // the floating point type. Test by simple to/from conversion. + // FIXME: Ideally the conversion to an APFloat and from an APFloat + // could be avoided if there was a convertFromAPInt method + // which could signal back if implicit truncation occurred. + llvm::APSInt Result = EVResult.Val.getInt(); + llvm::APFloat Float(S.Context.getFloatTypeSemantics(FloatTy)); + Float.convertFromAPInt(Result, IntTy->hasSignedIntegerRepresentation(), + llvm::APFloat::rmTowardZero); + llvm::APSInt ConvertBack(S.Context.getIntWidth(IntTy), + !IntTy->hasSignedIntegerRepresentation()); + bool Ignored = false; + Float.convertToInteger(ConvertBack, llvm::APFloat::rmNearestTiesToEven, + &Ignored); + if (Result != ConvertBack) + return true; + } else { + // Reject types that cannot be fully encoded into the mantissa of + // the float. + Bits = S.Context.getTypeSize(IntTy); + unsigned FloatPrec = llvm::APFloat::semanticsPrecision( + S.Context.getFloatTypeSemantics(FloatTy)); + if (Bits > FloatPrec) + return true; } -} -static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { - switch (CK) { - default: { -#ifndef NDEBUG - llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK) - << "\n"; -#endif - llvm_unreachable("unhandled cast kind"); - } - case CK_UserDefinedConversion: - return ICK_Identity; - case CK_LValueToRValue: - return ICK_Lvalue_To_Rvalue; - case CK_ArrayToPointerDecay: - return ICK_Array_To_Pointer; - case CK_FunctionToPointerDecay: - return ICK_Function_To_Pointer; - case CK_IntegralCast: - return ICK_Integral_Conversion; - case CK_FloatingCast: - return ICK_Floating_Conversion; - case CK_IntegralToFloating: - case CK_FloatingToIntegral: - return ICK_Floating_Integral; - case CK_IntegralComplexCast: - case CK_FloatingComplexCast: - case CK_FloatingComplexToIntegralComplex: - case CK_IntegralComplexToFloatingComplex: - return ICK_Complex_Conversion; - case CK_FloatingComplexToReal: - case CK_FloatingRealToComplex: - case CK_IntegralComplexToReal: - case CK_IntegralRealToComplex: - return ICK_Complex_Real; - case CK_HLSLArrayRValue: - return ICK_HLSL_Array_RValue; - } + return false; } -static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E, - QualType FromType, - SourceLocation Loc) { - // Check for a narrowing implicit conversion. - StandardConversionSequence SCS; - SCS.setAsIdentityConversion(); - SCS.setToType(0, FromType); - SCS.setToType(1, ToType); - if (const auto *ICE = dyn_cast(E)) - SCS.Second = castKindToImplicitConversionKind(ICE->getCastKind()); - - APValue PreNarrowingValue; - QualType PreNarrowingType; - switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, - PreNarrowingType, - /*IgnoreFloatToIntegralConversion*/ true)) { - case NK_Dependent_Narrowing: - // Implicit conversion to a narrower type, but the expression is - // value-dependent so we can't tell whether it's actually narrowing. - case NK_Not_Narrowing: - return false; +/// Attempt to convert and splat Scalar into a vector whose types matches +/// Vector following GCC conversion rules. The rule is that implicit +/// conversion can occur when Scalar can be casted to match Vector's element +/// type without causing truncation of Scalar. +static bool tryGCCVectorConvertAndSplat(Sema &S, ExprResult *Scalar, + ExprResult *Vector) { + QualType ScalarTy = Scalar->get()->getType().getUnqualifiedType(); + QualType VectorTy = Vector->get()->getType().getUnqualifiedType(); + QualType VectorEltTy; - case NK_Constant_Narrowing: - // Implicit conversion to a narrower type, and the value is not a constant - // expression. - S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) - << /*Constant*/ 1 - << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; - return true; + if (const auto *VT = VectorTy->getAs()) { + assert(!isa(VT) && + "ExtVectorTypes should not be handled here!"); + VectorEltTy = VT->getElementType(); + } else if (VectorTy->isSveVLSBuiltinType()) { + VectorEltTy = + VectorTy->castAs()->getSveEltType(S.getASTContext()); + } else { + llvm_unreachable("Only Fixed-Length and SVE Vector types are handled here"); + } - case NK_Variable_Narrowing: - // Implicit conversion to a narrower type, and the value is not a constant - // expression. - case NK_Type_Narrowing: - S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) - << /*Constant*/ 0 << FromType << ToType; - // TODO: It's not a constant expression, but what if the user intended it - // to be? Can we produce notes to help them figure out why it isn't? + // Reject cases where the vector element type or the scalar element type are + // not integral or floating point types. + if (!VectorEltTy->isArithmeticType() || !ScalarTy->isArithmeticType()) return true; - } - llvm_unreachable("unhandled case in switch"); -} -static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, - ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - // Dig out the original argument type and expression before implicit casts - // were applied. These are the types/expressions we need to check the - // [expr.spaceship] requirements against. - ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts(); - ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts(); - QualType LHSStrippedType = LHSStripped.get()->getType(); - QualType RHSStrippedType = RHSStripped.get()->getType(); + // The conversion to apply to the scalar before splatting it, + // if necessary. + CastKind ScalarCast = CK_NoOp; - // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the - // other is not, the program is ill-formed. - if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) { - S.InvalidOperands(Loc, LHSStripped, RHSStripped); - return QualType(); - } + // Accept cases where the vector elements are integers and the scalar is + // an integer. + // FIXME: Notionally if the scalar was a floating point value with a precise + // integral representation, we could cast it to an appropriate integer + // type and then perform the rest of the checks here. GCC will perform + // this conversion in some cases as determined by the input language. + // We should accept it on a language independent basis. + if (VectorEltTy->isIntegralType(S.Context) && + ScalarTy->isIntegralType(S.Context) && + S.Context.getIntegerTypeOrder(VectorEltTy, ScalarTy)) { - // FIXME: Consider combining this with checkEnumArithmeticConversions. - int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() + - RHSStrippedType->isEnumeralType(); - if (NumEnumArgs == 1) { - bool LHSIsEnum = LHSStrippedType->isEnumeralType(); - QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType; - if (OtherTy->hasFloatingRepresentation()) { - S.InvalidOperands(Loc, LHSStripped, RHSStripped); - return QualType(); - } - } - if (NumEnumArgs == 2) { - // C++2a [expr.spaceship]p5: If both operands have the same enumeration - // type E, the operator yields the result of converting the operands - // to the underlying type of E and applying <=> to the converted operands. - if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) { - S.InvalidOperands(Loc, LHS, RHS); - return QualType(); - } - QualType IntType = - LHSStrippedType->castAs()->getDecl()->getIntegerType(); - assert(IntType->isArithmeticType()); + if (canConvertIntToOtherIntTy(S, Scalar, VectorEltTy)) + return true; - // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we - // promote the boolean type, and all other promotable integer types, to - // avoid this. - if (S.Context.isPromotableIntegerType(IntType)) - IntType = S.Context.getPromotedIntegerType(IntType); + ScalarCast = CK_IntegralCast; + } else if (VectorEltTy->isIntegralType(S.Context) && + ScalarTy->isRealFloatingType()) { + if (S.Context.getTypeSize(VectorEltTy) == S.Context.getTypeSize(ScalarTy)) + ScalarCast = CK_FloatingToIntegral; + else + return true; + } else if (VectorEltTy->isRealFloatingType()) { + if (ScalarTy->isRealFloatingType()) { - LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast); - RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast); - LHSType = RHSType = IntType; - } + // Reject cases where the scalar type is not a constant and has a higher + // Order than the vector element type. + llvm::APFloat Result(0.0); - // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the - // usual arithmetic conversions are applied to the operands. - QualType Type = - S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); - - std::optional CCT = - getComparisonCategoryForBuiltinCmp(Type); - if (!CCT) - return S.InvalidOperands(Loc, LHS, RHS); - - bool HasNarrowing = checkThreeWayNarrowingConversion( - S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc()); - HasNarrowing |= checkThreeWayNarrowingConversion(S, Type, RHS.get(), RHSType, - RHS.get()->getBeginLoc()); - if (HasNarrowing) - return QualType(); - - assert(!Type.isNull() && "composite type for <=> has not been set"); - - return S.CheckComparisonCategoryType( - *CCT, Loc, Sema::ComparisonCategoryUsage::OperatorInExpression); -} - -static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) - return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); - - // C99 6.5.8p3 / C99 6.5.9p4 - QualType Type = - S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); - assert(Type->isArithmeticType() || Type->isEnumeralType()); + // Determine whether this is a constant scalar. In the event that the + // value is dependent (and thus cannot be evaluated by the constant + // evaluator), skip the evaluation. This will then diagnose once the + // expression is instantiated. + bool CstScalar = Scalar->get()->isValueDependent() || + Scalar->get()->EvaluateAsFloat(Result, S.Context); + int Order = S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy); + if (!CstScalar && Order < 0) + return true; - if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) - return S.InvalidOperands(Loc, LHS, RHS); + // If the scalar cannot be safely casted to the vector element type, + // reject it. + if (CstScalar) { + bool Truncated = false; + Result.convert(S.Context.getFloatTypeSemantics(VectorEltTy), + llvm::APFloat::rmNearestTiesToEven, &Truncated); + if (Truncated) + return true; + } - // Check for comparisons of floating point operands using != and ==. - if (Type->hasFloatingRepresentation()) - S.CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + ScalarCast = CK_FloatingCast; + } else if (ScalarTy->isIntegralType(S.Context)) { + if (canConvertIntTyToFloatTy(S, Scalar, VectorEltTy)) + return true; - // The result of comparisons is 'bool' in C++, 'int' in C. - return S.Context.getLogicalOperationType(); -} + ScalarCast = CK_IntegralToFloating; + } else + return true; + } else if (ScalarTy->isEnumeralType()) + return true; -void Sema::CheckPtrComparisonWithNullChar(ExprResult &E, ExprResult &NullE) { - if (!NullE.get()->getType()->isAnyPointerType()) - return; - int NullValue = PP.isMacroDefined("NULL") ? 0 : 1; - if (!E.get()->getType()->isAnyPointerType() && - E.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull) == - Expr::NPCK_ZeroExpression) { - if (const auto *CL = dyn_cast(E.get())) { - if (CL->getValue() == 0) - Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) - << NullValue - << FixItHint::CreateReplacement(E.get()->getExprLoc(), - NullValue ? "NULL" : "(void *)0"); - } else if (const auto *CE = dyn_cast(E.get())) { - TypeSourceInfo *TI = CE->getTypeInfoAsWritten(); - QualType T = Context.getCanonicalType(TI->getType()).getUnqualifiedType(); - if (T == Context.CharTy) - Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) - << NullValue - << FixItHint::CreateReplacement(E.get()->getExprLoc(), - NullValue ? "NULL" : "(void *)0"); - } - } + // Adjust scalar if desired. + if (ScalarCast != CK_NoOp) + *Scalar = S.ImpCastExprToType(Scalar->get(), VectorEltTy, ScalarCast); + *Scalar = S.ImpCastExprToType(Scalar->get(), VectorTy, CK_VectorSplat); + return false; } -// C99 6.5.8, C++ [expr.rel] -QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - bool IsRelational = BinaryOperator::isRelationalOp(Opc); - bool IsThreeWay = Opc == BO_Cmp; - bool IsOrdered = IsRelational || IsThreeWay; - auto IsAnyPointerType = [](ExprResult E) { - QualType Ty = E.get()->getType(); - return Ty->isPointerType() || Ty->isMemberPointerType(); - }; - - // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer - // type, array-to-pointer, ..., conversions are performed on both operands to - // bring them to their composite type. - // Otherwise, all comparisons expect an rvalue, so convert to rvalue before - // any type-related checks. - if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) { +QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompAssign, + bool AllowBothBool, + bool AllowBoolConversions, + bool AllowBoolOperation, + bool ReportInvalid) { + if (!IsCompAssign) { LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); if (LHS.isInvalid()) return QualType(); - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - } else { - LHS = DefaultLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - RHS = DefaultLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/true); - if (!getLangOpts().CPlusPlus && BinaryOperator::isEqualityOp(Opc)) { - CheckPtrComparisonWithNullChar(LHS, RHS); - CheckPtrComparisonWithNullChar(RHS, LHS); - } + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); - // Handle vector comparisons separately. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorCompareOperands(LHS, RHS, Loc, Opc); + const VectorType *LHSVecType = LHSType->getAs(); + const VectorType *RHSVecType = RHSType->getAs(); + assert(LHSVecType || RHSVecType); - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) - return CheckSizelessVectorCompareOperands(LHS, RHS, Loc, Opc); + // AltiVec-style "vector bool op vector bool" combinations are allowed + // for some operators but not others. + if (!AllowBothBool && LHSVecType && + LHSVecType->getVectorKind() == VectorKind::AltiVecBool && RHSVecType && + RHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); - diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + // This operation may not be performed on boolean vectors. + if (!AllowBoolOperation && + (LHSType->isExtVectorBoolType() || RHSType->isExtVectorBoolType())) + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - if ((LHSType->isArithmeticType() || LHSType->isEnumeralType()) && - (RHSType->isArithmeticType() || RHSType->isEnumeralType())) - return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); + // If the vector types are identical, return. + if (Context.hasSameType(LHSType, RHSType)) + return Context.getCommonSugaredType(LHSType, RHSType); - if ((LHSType->isPointerType() && - LHSType->getPointeeType().isWebAssemblyReferenceType()) || - (RHSType->isPointerType() && - RHSType->getPointeeType().isWebAssemblyReferenceType())) - return InvalidOperands(Loc, LHS, RHS); + // If we have compatible AltiVec and GCC vector types, use the AltiVec type. + if (LHSVecType && RHSVecType && + Context.areCompatibleVectorTypes(LHSType, RHSType)) { + if (isa(LHSVecType)) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return LHSType; + } - const Expr::NullPointerConstantKind LHSNullKind = - LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); - const Expr::NullPointerConstantKind RHSNullKind = - RHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); - bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull; - bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull; + if (!IsCompAssign) + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + return RHSType; + } - auto computeResultTy = [&]() { - if (Opc != BO_Cmp) - return Context.getLogicalOperationType(); - assert(getLangOpts().CPlusPlus); - assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); + // AllowBoolConversions says that bool and non-bool AltiVec vectors + // can be mixed, with the result being the non-bool type. The non-bool + // operand must have integer element type. + if (AllowBoolConversions && LHSVecType && RHSVecType && + LHSVecType->getNumElements() == RHSVecType->getNumElements() && + (Context.getTypeSize(LHSVecType->getElementType()) == + Context.getTypeSize(RHSVecType->getElementType()))) { + if (LHSVecType->getVectorKind() == VectorKind::AltiVecVector && + LHSVecType->getElementType()->isIntegerType() && + RHSVecType->getVectorKind() == VectorKind::AltiVecBool) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return LHSType; + } + if (!IsCompAssign && + LHSVecType->getVectorKind() == VectorKind::AltiVecBool && + RHSVecType->getVectorKind() == VectorKind::AltiVecVector && + RHSVecType->getElementType()->isIntegerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + return RHSType; + } + } - QualType CompositeTy = LHS.get()->getType(); - assert(!CompositeTy->isReferenceType()); + // Expressions containing fixed-length and sizeless SVE/RVV vectors are + // invalid since the ambiguity can affect the ABI. + auto IsSveRVVConversion = [](QualType FirstType, QualType SecondType, + unsigned &SVEorRVV) { + const VectorType *VecType = SecondType->getAs(); + SVEorRVV = 0; + if (FirstType->isSizelessBuiltinType() && VecType) { + if (VecType->getVectorKind() == VectorKind::SveFixedLengthData || + VecType->getVectorKind() == VectorKind::SveFixedLengthPredicate) + return true; + if (VecType->getVectorKind() == VectorKind::RVVFixedLengthData || + VecType->getVectorKind() == VectorKind::RVVFixedLengthMask) { + SVEorRVV = 1; + return true; + } + } - std::optional CCT = - getComparisonCategoryForBuiltinCmp(CompositeTy); - if (!CCT) - return InvalidOperands(Loc, LHS, RHS); + return false; + }; - if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) { - // P0946R0: Comparisons between a null pointer constant and an object - // pointer result in std::strong_equality, which is ill-formed under - // P1959R0. - Diag(Loc, diag::err_typecheck_three_way_comparison_of_pointer_and_zero) - << (LHSIsNull ? LHS.get()->getSourceRange() - : RHS.get()->getSourceRange()); - return QualType(); - } - - return CheckComparisonCategoryType( - *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); - }; - - if (!IsOrdered && LHSIsNull != RHSIsNull) { - bool IsEquality = Opc == BO_EQ; - if (RHSIsNull) - DiagnoseAlwaysNonNullPointer(LHS.get(), RHSNullKind, IsEquality, - RHS.get()->getSourceRange()); - else - DiagnoseAlwaysNonNullPointer(RHS.get(), LHSNullKind, IsEquality, - LHS.get()->getSourceRange()); - } - - if (IsOrdered && LHSType->isFunctionPointerType() && - RHSType->isFunctionPointerType()) { - // Valid unless a relational comparison of function pointers - bool IsError = Opc == BO_Cmp; - auto DiagID = - IsError ? diag::err_typecheck_ordered_comparison_of_function_pointers - : getLangOpts().CPlusPlus - ? diag::warn_typecheck_ordered_comparison_of_function_pointers - : diag::ext_typecheck_ordered_comparison_of_function_pointers; - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - if (IsError) - return QualType(); + unsigned SVEorRVV; + if (IsSveRVVConversion(LHSType, RHSType, SVEorRVV) || + IsSveRVVConversion(RHSType, LHSType, SVEorRVV)) { + Diag(Loc, diag::err_typecheck_sve_rvv_ambiguous) + << SVEorRVV << LHSType << RHSType; + return QualType(); } - if ((LHSType->isIntegerType() && !LHSIsNull) || - (RHSType->isIntegerType() && !RHSIsNull)) { - // Skip normal pointer conversion checks in this case; we have better - // diagnostics for this below. - } else if (getLangOpts().CPlusPlus) { - // Equality comparison of a function pointer to a void pointer is invalid, - // but we allow it as an extension. - // FIXME: If we really want to allow this, should it be part of composite - // pointer type computation so it works in conditionals too? - if (!IsOrdered && - ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) || - (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) { - // This is a gcc extension compatibility comparison. - // In a SFINAE context, we treat this as a hard error to maintain - // conformance with the C++ standard. - diagnoseFunctionPointerToVoidComparison( - *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); - - if (isSFINAEContext()) - return QualType(); - - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); - } - - // C++ [expr.eq]p2: - // If at least one operand is a pointer [...] bring them to their - // composite pointer type. - // C++ [expr.spaceship]p6 - // If at least one of the operands is of pointer type, [...] bring them - // to their composite pointer type. - // C++ [expr.rel]p2: - // If both operands are pointers, [...] bring them to their composite - // pointer type. - // For <=>, the only valid non-pointer types are arrays and functions, and - // we already decayed those, so this is really the same as the relational - // comparison rule. - if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= - (IsOrdered ? 2 : 1) && - (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || - RHSType->isObjCObjectPointerType()))) { - if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) - return QualType(); - return computeResultTy(); - } - } else if (LHSType->isPointerType() && - RHSType->isPointerType()) { // C99 6.5.8p2 - // All of the following pointer-related warnings are GCC extensions, except - // when handling null pointer constants. - QualType LCanPointeeTy = - LHSType->castAs()->getPointeeType().getCanonicalType(); - QualType RCanPointeeTy = - RHSType->castAs()->getPointeeType().getCanonicalType(); + // Expressions containing GNU and SVE or RVV (fixed or sizeless) vectors are + // invalid since the ambiguity can affect the ABI. + auto IsSveRVVGnuConversion = [](QualType FirstType, QualType SecondType, + unsigned &SVEorRVV) { + const VectorType *FirstVecType = FirstType->getAs(); + const VectorType *SecondVecType = SecondType->getAs(); - // C99 6.5.9p2 and C99 6.5.8p2 - if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), - RCanPointeeTy.getUnqualifiedType())) { - if (IsRelational) { - // Pointers both need to point to complete or incomplete types - if ((LCanPointeeTy->isIncompleteType() != - RCanPointeeTy->isIncompleteType()) && - !getLangOpts().C11) { - Diag(Loc, diag::ext_typecheck_compare_complete_incomplete_pointers) - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange() - << LHSType << RHSType << LCanPointeeTy->isIncompleteType() - << RCanPointeeTy->isIncompleteType(); + SVEorRVV = 0; + if (FirstVecType && SecondVecType) { + if (FirstVecType->getVectorKind() == VectorKind::Generic) { + if (SecondVecType->getVectorKind() == VectorKind::SveFixedLengthData || + SecondVecType->getVectorKind() == + VectorKind::SveFixedLengthPredicate) + return true; + if (SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthData || + SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthMask) { + SVEorRVV = 1; + return true; } } - } else if (!IsRelational && - (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) { - // Valid unless comparison between non-null pointer and function pointer - if ((LCanPointeeTy->isFunctionType() || RCanPointeeTy->isFunctionType()) - && !LHSIsNull && !RHSIsNull) - diagnoseFunctionPointerToVoidComparison(*this, Loc, LHS, RHS, - /*isError*/false); - } else { - // Invalid - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); + return false; } - if (LCanPointeeTy != RCanPointeeTy) { - // Treat NULL constant as a special case in OpenCL. - if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { - if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy)) { - Diag(Loc, - diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSType << RHSType << 0 /* comparison */ - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - } + + if (SecondVecType && + SecondVecType->getVectorKind() == VectorKind::Generic) { + if (FirstType->isSVESizelessBuiltinType()) + return true; + if (FirstType->isRVVSizelessBuiltinType()) { + SVEorRVV = 1; + return true; } - LangAS AddrSpaceL = LCanPointeeTy.getAddressSpace(); - LangAS AddrSpaceR = RCanPointeeTy.getAddressSpace(); - CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion - : CK_BitCast; - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, Kind); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); } - return computeResultTy(); - } + return false; + }; - // C++ [expr.eq]p4: - // Two operands of type std::nullptr_t or one operand of type - // std::nullptr_t and the other a null pointer constant compare - // equal. - // C23 6.5.9p5: - // If both operands have type nullptr_t or one operand has type nullptr_t - // and the other is a null pointer constant, they compare equal if the - // former is a null pointer. - if (!IsOrdered && LHSIsNull && RHSIsNull) { - if (LHSType->isNullPtrType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (RHSType->isNullPtrType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } + if (IsSveRVVGnuConversion(LHSType, RHSType, SVEorRVV) || + IsSveRVVGnuConversion(RHSType, LHSType, SVEorRVV)) { + Diag(Loc, diag::err_typecheck_sve_rvv_gnu_ambiguous) + << SVEorRVV << LHSType << RHSType; + return QualType(); } - if (!getLangOpts().CPlusPlus && !IsOrdered && (LHSIsNull || RHSIsNull)) { - // C23 6.5.9p6: - // Otherwise, at least one operand is a pointer. If one is a pointer and - // the other is a null pointer constant or has type nullptr_t, they - // compare equal - if (LHSIsNull && RHSType->isPointerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); + // If there's a vector type and a scalar, try to convert the scalar to + // the vector element type and splat. + unsigned DiagID = diag::err_typecheck_vector_not_convertable; + if (!RHSVecType) { + if (isa(LHSVecType)) { + if (!tryVectorConvertAndSplat(*this, &RHS, RHSType, + LHSVecType->getElementType(), LHSType, + DiagID)) + return LHSType; + } else { + if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) + return LHSType; } - if (RHSIsNull && LHSType->isPointerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); + } + if (!LHSVecType) { + if (isa(RHSVecType)) { + if (!tryVectorConvertAndSplat(*this, (IsCompAssign ? nullptr : &LHS), + LHSType, RHSVecType->getElementType(), + RHSType, DiagID)) + return RHSType; + } else { + if (LHS.get()->isLValue() || + !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) + return RHSType; } } - // Comparison of Objective-C pointers and block pointers against nullptr_t. - // These aren't covered by the composite pointer type rules. - if (!IsOrdered && RHSType->isNullPtrType() && - (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (!IsOrdered && LHSType->isNullPtrType() && - (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); + // FIXME: The code below also handles conversion between vectors and + // non-scalars, we should break this down into fine grained specific checks + // and emit proper diagnostics. + QualType VecType = LHSVecType ? LHSType : RHSType; + const VectorType *VT = LHSVecType ? LHSVecType : RHSVecType; + QualType OtherType = LHSVecType ? RHSType : LHSType; + ExprResult *OtherExpr = LHSVecType ? &RHS : &LHS; + if (isLaxVectorConversion(OtherType, VecType)) { + if (Context.getTargetInfo().getTriple().isPPC() && + anyAltivecTypes(RHSType, LHSType) && + !Context.areCompatibleVectorTypes(RHSType, LHSType)) + Diag(Loc, diag::warn_deprecated_lax_vec_conv_all) << RHSType << LHSType; + // If we're allowing lax vector conversions, only the total (data) size + // needs to be the same. For non compound assignment, if one of the types is + // scalar, the result is always the vector type. + if (!IsCompAssign) { + *OtherExpr = ImpCastExprToType(OtherExpr->get(), VecType, CK_BitCast); + return VecType; + // In a compound assignment, lhs += rhs, 'lhs' is a lvalue src, forbidding + // any implicit cast. Here, the 'rhs' should be implicit casted to 'lhs' + // type. Note that this is already done by non-compound assignments in + // CheckAssignmentConstraints. If it's a scalar type, only bitcast for + // <1 x T> -> T. The result is also a vector type. + } else if (OtherType->isExtVectorType() || OtherType->isVectorType() || + (OtherType->isScalarType() && VT->getNumElements() == 1)) { + ExprResult *RHSExpr = &RHS; + *RHSExpr = ImpCastExprToType(RHSExpr->get(), LHSType, CK_BitCast); + return VecType; + } } - if (getLangOpts().CPlusPlus) { - if (IsRelational && - ((LHSType->isNullPtrType() && RHSType->isPointerType()) || - (RHSType->isNullPtrType() && LHSType->isPointerType()))) { - // HACK: Relational comparison of nullptr_t against a pointer type is - // invalid per DR583, but we allow it within std::less<> and friends, - // since otherwise common uses of it break. - // FIXME: Consider removing this hack once LWG fixes std::less<> and - // friends to have std::nullptr_t overload candidates. - DeclContext *DC = CurContext; - if (isa(DC)) - DC = DC->getParent(); - if (auto *CTSD = dyn_cast(DC)) { - if (CTSD->isInStdNamespace() && - llvm::StringSwitch(CTSD->getName()) - .Cases("less", "less_equal", "greater", "greater_equal", true) - .Default(false)) { - if (RHSType->isNullPtrType()) - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - else - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } - } - } + // Okay, the expression is invalid. - // C++ [expr.eq]p2: - // If at least one operand is a pointer to member, [...] bring them to - // their composite pointer type. - if (!IsOrdered && - (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { - if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) - return QualType(); - else - return computeResultTy(); - } + // If there's a non-vector, non-real operand, diagnose that. + if ((!RHSVecType && !RHSType->isRealType()) || + (!LHSVecType && !LHSType->isRealType())) { + Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) + << LHSType << RHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } - // Handle block pointer types. - if (!IsOrdered && LHSType->isBlockPointerType() && - RHSType->isBlockPointerType()) { - QualType lpointee = LHSType->castAs()->getPointeeType(); - QualType rpointee = RHSType->castAs()->getPointeeType(); + // OpenCL V1.1 6.2.6.p1: + // If the operands are of more than one vector type, then an error shall + // occur. Implicit conversions between vector types are not permitted, per + // section 6.2.1. + if (getLangOpts().OpenCL && + RHSVecType && isa(RHSVecType) && + LHSVecType && isa(LHSVecType)) { + Diag(Loc, diag::err_opencl_implicit_vector_conversion) << LHSType + << RHSType; + return QualType(); + } - if (!LHSIsNull && !RHSIsNull && - !Context.typesAreCompatible(lpointee, rpointee)) { - Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - } - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); + + // If there is a vector type that is not a ExtVector and a scalar, we reach + // this point if scalar could not be converted to the vector's element type + // without truncation. + if ((RHSVecType && !isa(RHSVecType)) || + (LHSVecType && !isa(LHSVecType))) { + QualType Scalar = LHSVecType ? RHSType : LHSType; + QualType Vector = LHSVecType ? LHSType : RHSType; + unsigned ScalarOrVector = LHSVecType && RHSVecType ? 1 : 0; + Diag(Loc, + diag::err_typecheck_vector_not_convertable_implict_truncation) + << ScalarOrVector << Scalar << Vector; + + return QualType(); } - // Allow block pointers to be compared with null pointer constants. - if (!IsOrdered - && ((LHSType->isBlockPointerType() && RHSType->isPointerType()) - || (LHSType->isPointerType() && RHSType->isBlockPointerType()))) { - if (!LHSIsNull && !RHSIsNull) { - if (!((RHSType->isPointerType() && RHSType->castAs() - ->getPointeeType()->isVoidType()) - || (LHSType->isPointerType() && LHSType->castAs() - ->getPointeeType()->isVoidType()))) - Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - } - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, - RHSType->isPointerType() ? CK_BitCast - : CK_AnyPointerToBlockPointerCast); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, - LHSType->isPointerType() ? CK_BitCast - : CK_AnyPointerToBlockPointerCast); - return computeResultTy(); + // Otherwise, use the generic diagnostic. + Diag(Loc, DiagID) + << LHSType << RHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); +} + +QualType Sema::CheckSizelessVectorOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign, + ArithConvKind OperationKind) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - if (LHSType->isObjCObjectPointerType() || - RHSType->isObjCObjectPointerType()) { - const PointerType *LPT = LHSType->getAs(); - const PointerType *RPT = RHSType->getAs(); - if (LPT || RPT) { - bool LPtrToVoid = LPT ? LPT->getPointeeType()->isVoidType() : false; - bool RPtrToVoid = RPT ? RPT->getPointeeType()->isVoidType() : false; + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); - if (!LPtrToVoid && !RPtrToVoid && - !Context.typesAreCompatible(LHSType, RHSType)) { - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, - /*isError*/false); - } - // FIXME: If LPtrToVoid, we should presumably convert the LHS rather than - // the RHS, but we have test coverage for this behavior. - // FIXME: Consider using convertPointersToCompositeType in C++. - if (LHSIsNull && !RHSIsNull) { - Expr *E = LHS.get(); - if (getLangOpts().ObjCAutoRefCount) - ObjC().CheckObjCConversion(SourceRange(), RHSType, E, - CheckedConversionKind::Implicit); - LHS = ImpCastExprToType(E, RHSType, - RPT ? CK_BitCast :CK_CPointerToObjCPointerCast); - } - else { - Expr *E = RHS.get(); - if (getLangOpts().ObjCAutoRefCount) - ObjC().CheckObjCConversion(SourceRange(), LHSType, E, - CheckedConversionKind::Implicit, - /*Diagnose=*/true, - /*DiagnoseCFAudited=*/false, Opc); - RHS = ImpCastExprToType(E, LHSType, - LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); - } - return computeResultTy(); - } - if (LHSType->isObjCObjectPointerType() && - RHSType->isObjCObjectPointerType()) { - if (!Context.areComparableObjCPointerTypes(LHSType, RHSType)) - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, - /*isError*/false); - if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS)) - diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc); + const BuiltinType *LHSBuiltinTy = LHSType->getAs(); + const BuiltinType *RHSBuiltinTy = RHSType->getAs(); - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); - } + unsigned DiagID = diag::err_typecheck_invalid_operands; + if ((OperationKind == ACK_Arithmetic) && + ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || + (RHSBuiltinTy && RHSBuiltinTy->isSVEBool()))) { + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - if (!IsOrdered && LHSType->isBlockPointerType() && - RHSType->isBlockCompatibleObjCPointerType(Context)) { - LHS = ImpCastExprToType(LHS.get(), RHSType, - CK_BlockPointerToObjCPointerCast); - return computeResultTy(); - } else if (!IsOrdered && - LHSType->isBlockCompatibleObjCPointerType(Context) && - RHSType->isBlockPointerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, - CK_BlockPointerToObjCPointerCast); - return computeResultTy(); - } + if (Context.hasSameType(LHSType, RHSType)) + return LHSType; + + if (LHSType->isSveVLSBuiltinType() && !RHSType->isSveVLSBuiltinType()) { + if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) + return LHSType; + } + if (RHSType->isSveVLSBuiltinType() && !LHSType->isSveVLSBuiltinType()) { + if (LHS.get()->isLValue() || + !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) + return RHSType; } - if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || - (LHSType->isIntegerType() && RHSType->isAnyPointerType())) { - unsigned DiagID = 0; - bool isError = false; - if (LangOpts.DebuggerSupport) { - // Under a debugger, allow the comparison of pointers to integers, - // since users tend to want to compare addresses. - } else if ((LHSIsNull && LHSType->isIntegerType()) || - (RHSIsNull && RHSType->isIntegerType())) { - if (IsOrdered) { - isError = getLangOpts().CPlusPlus; - DiagID = - isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero - : diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; - } - } else if (getLangOpts().CPlusPlus) { - DiagID = diag::err_typecheck_comparison_of_pointer_integer; - isError = true; - } else if (IsOrdered) - DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; - else - DiagID = diag::ext_typecheck_comparison_of_pointer_integer; - if (DiagID) { - Diag(Loc, DiagID) + if ((!LHSType->isSveVLSBuiltinType() && !LHSType->isRealType()) || + (!RHSType->isSveVLSBuiltinType() && !RHSType->isRealType())) { + Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) << LHSType << RHSType << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - if (isError) - return QualType(); - } + return QualType(); + } - if (LHSType->isIntegerType()) - LHS = ImpCastExprToType(LHS.get(), RHSType, - LHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, - RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - return computeResultTy(); + if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && + Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != + Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC) { + Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - // Handle block pointers. - if (!IsOrdered && RHSIsNull - && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (!IsOrdered && LHSIsNull - && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } - - if (getLangOpts().getOpenCLCompatibleVersion() >= 200) { - if (LHSType->isClkEventT() && RHSType->isClkEventT()) { - return computeResultTy(); - } - - if (LHSType->isQueueT() && RHSType->isQueueT()) { - return computeResultTy(); - } + if (LHSType->isSveVLSBuiltinType() || RHSType->isSveVLSBuiltinType()) { + QualType Scalar = LHSType->isSveVLSBuiltinType() ? RHSType : LHSType; + QualType Vector = LHSType->isSveVLSBuiltinType() ? LHSType : RHSType; + bool ScalarOrVector = + LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType(); - if (LHSIsNull && RHSType->isQueueT()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } + Diag(Loc, diag::err_typecheck_vector_not_convertable_implict_truncation) + << ScalarOrVector << Scalar << Vector; - if (LHSType->isQueueT() && RHSIsNull) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } + return QualType(); } - return InvalidOperands(Loc, LHS, RHS); + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } -QualType Sema::GetSignedVectorType(QualType V) { - const VectorType *VTy = V->castAs(); - unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); +// checkArithmeticNull - Detect when a NULL constant is used improperly in an +// expression. These are mainly cases where the null pointer is used as an +// integer instead of a pointer. +static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompare) { + // The canonical way to check for a GNU null is with isNullPointerConstant, + // but we use a bit of a hack here for speed; this is a relatively + // hot path, and isNullPointerConstant is slow. + bool LHSNull = isa(LHS.get()->IgnoreParenImpCasts()); + bool RHSNull = isa(RHS.get()->IgnoreParenImpCasts()); - if (isa(VTy)) { - if (VTy->isExtVectorBoolType()) - return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.CharTy)) - return Context.getExtVectorType(Context.CharTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getExtVectorType(Context.IntTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getExtVectorType(Context.LongTy, VTy->getNumElements()); - assert(TypeSize == Context.getTypeSize(Context.LongLongTy) && - "Unhandled vector element size in vector compare"); - return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements()); - } + QualType NonNullType = LHSNull ? RHS.get()->getType() : LHS.get()->getType(); - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongLongTy)) - return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getVectorType(Context.LongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getVectorType(Context.IntTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getVectorType(Context.ShortTy, VTy->getNumElements(), - VectorKind::Generic); - assert(TypeSize == Context.getTypeSize(Context.CharTy) && - "Unhandled vector element size in vector compare"); - return Context.getVectorType(Context.CharTy, VTy->getNumElements(), - VectorKind::Generic); -} + // Avoid analyzing cases where the result will either be invalid (and + // diagnosed as such) or entirely valid and not something to warn about. + if ((!LHSNull && !RHSNull) || NonNullType->isBlockPointerType() || + NonNullType->isMemberPointerType() || NonNullType->isFunctionType()) + return; -QualType Sema::GetSignedSizelessVectorType(QualType V) { - const BuiltinType *VTy = V->castAs(); - assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); + // Comparison operations would not make sense with a null pointer no matter + // what the other expression is. + if (!IsCompare) { + S.Diag(Loc, diag::warn_null_in_arithmetic_operation) + << (LHSNull ? LHS.get()->getSourceRange() : SourceRange()) + << (RHSNull ? RHS.get()->getSourceRange() : SourceRange()); + return; + } - const QualType ETy = V->getSveEltType(Context); - const auto TypeSize = Context.getTypeSize(ETy); + // The rest of the operations only make sense with a null pointer + // if the other expression is a pointer. + if (LHSNull == RHSNull || NonNullType->isAnyPointerType() || + NonNullType->canDecayToPointerType()) + return; - const QualType IntTy = Context.getIntTypeForBitwidth(TypeSize, true); - const llvm::ElementCount VecSize = Context.getBuiltinVectorTypeInfo(VTy).EC; - return Context.getScalableVectorType(IntTy, VecSize.getKnownMinValue()); + S.Diag(Loc, diag::warn_null_in_comparison_operation) + << LHSNull /* LHS is NULL */ << NonNullType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); } -QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) { - Diag(Loc, diag::err_three_way_vector_comparison); - return QualType(); - } +static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, + SourceLocation Loc) { + const auto *LUE = dyn_cast(LHS); + const auto *RUE = dyn_cast(RHS); + if (!LUE || !RUE) + return; + if (LUE->getKind() != UETT_SizeOf || LUE->isArgumentType() || + RUE->getKind() != UETT_SizeOf) + return; - // Check to make sure we're operating on vectors of the same type and width, - // Allowing one side to be a scalar of element type. - QualType vType = - CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ true, - /*ReportInvalid*/ true); - if (vType.isNull()) - return vType; + const Expr *LHSArg = LUE->getArgumentExpr()->IgnoreParens(); + QualType LHSTy = LHSArg->getType(); + QualType RHSTy; - QualType LHSType = LHS.get()->getType(); + if (RUE->isArgumentType()) + RHSTy = RUE->getArgumentType().getNonReferenceType(); + else + RHSTy = RUE->getArgumentExpr()->IgnoreParens()->getType(); - // Determine the return type of a vector compare. By default clang will return - // a scalar for all vector compares except vector bool and vector pixel. - // With the gcc compiler we will always return a vector type and with the xl - // compiler we will always return a scalar type. This switch allows choosing - // which behavior is prefered. - if (getLangOpts().AltiVec) { - switch (getLangOpts().getAltivecSrcCompat()) { - case LangOptions::AltivecSrcCompatKind::Mixed: - // If AltiVec, the comparison results in a numeric type, i.e. - // bool for C++, int for C - if (vType->castAs()->getVectorKind() == - VectorKind::AltiVecVector) - return Context.getLogicalOperationType(); - else - Diag(Loc, diag::warn_deprecated_altivec_src_compat); - break; - case LangOptions::AltivecSrcCompatKind::GCC: - // For GCC we always return the vector type. - break; - case LangOptions::AltivecSrcCompatKind::XL: - return Context.getLogicalOperationType(); - break; - } - } + if (LHSTy->isPointerType() && !RHSTy->isPointerType()) { + if (!S.Context.hasSameUnqualifiedType(LHSTy->getPointeeType(), RHSTy)) + return; - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + S.Diag(Loc, diag::warn_division_sizeof_ptr) << LHS << LHS->getSourceRange(); + if (const auto *DRE = dyn_cast(LHSArg)) { + if (const ValueDecl *LHSArgDecl = DRE->getDecl()) + S.Diag(LHSArgDecl->getLocation(), diag::note_pointer_declared_here) + << LHSArgDecl; + } + } else if (const auto *ArrayTy = S.Context.getAsArrayType(LHSTy)) { + QualType ArrayElemTy = ArrayTy->getElementType(); + if (ArrayElemTy != S.Context.getBaseElementType(ArrayTy) || + ArrayElemTy->isDependentType() || RHSTy->isDependentType() || + RHSTy->isReferenceType() || ArrayElemTy->isCharType() || + S.Context.getTypeSize(ArrayElemTy) == S.Context.getTypeSize(RHSTy)) + return; + S.Diag(Loc, diag::warn_division_sizeof_array) + << LHSArg->getSourceRange() << ArrayElemTy << RHSTy; + if (const auto *DRE = dyn_cast(LHSArg)) { + if (const ValueDecl *LHSArgDecl = DRE->getDecl()) + S.Diag(LHSArgDecl->getLocation(), diag::note_array_declared_here) + << LHSArgDecl; + } - // Check for comparisons of floating point operands using != and ==. - if (LHSType->hasFloatingRepresentation()) { - assert(RHS.get()->getType()->hasFloatingRepresentation()); - CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + S.Diag(Loc, diag::note_precedence_silence) << RHS; } +} - // Return a signed type for the vector. - return GetSignedVectorType(vType); +static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, bool IsDiv) { + // Check for division/remainder by zero. + Expr::EvalResult RHSValue; + if (!RHS.get()->isValueDependent() && + RHS.get()->EvaluateAsInt(RHSValue, S.Context) && + RHSValue.Val.getInt() == 0) + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_remainder_division_by_zero) + << IsDiv << RHS.get()->getSourceRange()); } -QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) { - Diag(Loc, diag::err_three_way_vector_comparison); - return QualType(); - } +QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign, bool IsDiv) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - // Check to make sure we're operating on vectors of the same type and width, - // Allowing one side to be a scalar of element type. - QualType vType = CheckSizelessVectorOperands( - LHS, RHS, Loc, /*isCompAssign*/ false, ACK_Comparison); + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + if (LHSTy->isVectorType() || RHSTy->isVectorType()) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (LHSTy->isSveVLSBuiltinType() || RHSTy->isSveVLSBuiltinType()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_Arithmetic); + if (!IsDiv && + (LHSTy->isConstantMatrixType() || RHSTy->isConstantMatrixType())) + return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); + // For division, only matrix-by-scalar is supported. Other combinations with + // matrix types are invalid. + if (IsDiv && LHSTy->isConstantMatrixType() && RHSTy->isArithmeticType()) + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); - if (vType.isNull()) - return vType; + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); - QualType LHSType = LHS.get()->getType(); - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + if (compType.isNull() || !compType->isArithmeticType()) + return InvalidOperands(Loc, LHS, RHS); + if (IsDiv) { + DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); + DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc); + } + return compType; +} - // Check for comparisons of floating point operands using != and ==. - if (LHSType->hasFloatingRepresentation()) { - assert(RHS.get()->getType()->hasFloatingRepresentation()); - CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); +QualType Sema::CheckRemainderOperands( + ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + return InvalidOperands(Loc, LHS, RHS); } - const BuiltinType *LHSBuiltinTy = LHSType->getAs(); - const BuiltinType *RHSBuiltinTy = RHS.get()->getType()->getAs(); + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_Arithmetic); - if (LHSBuiltinTy && RHSBuiltinTy && LHSBuiltinTy->isSVEBool() && - RHSBuiltinTy->isSVEBool()) - return LHSType; + return InvalidOperands(Loc, LHS, RHS); + } - // Return a signed type for the vector. - return GetSignedSizelessVectorType(vType); + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + + if (compType.isNull() || !compType->isIntegerType()) + return InvalidOperands(Loc, LHS, RHS); + DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); + return compType; } -static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS, - const ExprResult &XorRHS, - const SourceLocation Loc) { - // Do not diagnose macros. - if (Loc.isMacroID()) - return; +/// Diagnose invalid arithmetic on two void pointers. +static void diagnoseArithmeticOnTwoVoidPointers(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 1 /* two pointers */ << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); +} - // Do not diagnose if both LHS and RHS are macros. - if (XorLHS.get()->getExprLoc().isMacroID() && - XorRHS.get()->getExprLoc().isMacroID()) - return; +/// Diagnose invalid arithmetic on a void pointer. +static void diagnoseArithmeticOnVoidPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 0 /* one pointer */ << Pointer->getSourceRange(); +} - bool Negative = false; - bool ExplicitPlus = false; - const auto *LHSInt = dyn_cast(XorLHS.get()); - const auto *RHSInt = dyn_cast(XorRHS.get()); +/// Diagnose invalid arithmetic on a null pointer. +/// +/// If \p IsGNUIdiom is true, the operation is using the 'p = (i8*)nullptr + n' +/// idiom, which we recognize as a GNU extension. +/// +static void diagnoseArithmeticOnNullPointer(Sema &S, SourceLocation Loc, + Expr *Pointer, bool IsGNUIdiom) { + if (IsGNUIdiom) + S.Diag(Loc, diag::warn_gnu_null_ptr_arith) + << Pointer->getSourceRange(); + else + S.Diag(Loc, diag::warn_pointer_arith_null_ptr) + << S.getLangOpts().CPlusPlus << Pointer->getSourceRange(); +} - if (!LHSInt) +/// Diagnose invalid subraction on a null pointer. +/// +static void diagnoseSubtractionOnNullPointer(Sema &S, SourceLocation Loc, + Expr *Pointer, bool BothNull) { + // Null - null is valid in C++ [expr.add]p7 + if (BothNull && S.getLangOpts().CPlusPlus) return; - if (!RHSInt) { - // Check negative literals. - if (const auto *UO = dyn_cast(XorRHS.get())) { - UnaryOperatorKind Opc = UO->getOpcode(); - if (Opc != UO_Minus && Opc != UO_Plus) - return; - RHSInt = dyn_cast(UO->getSubExpr()); - if (!RHSInt) - return; - Negative = (Opc == UO_Minus); - ExplicitPlus = !Negative; - } else { - return; - } - } - const llvm::APInt &LeftSideValue = LHSInt->getValue(); - llvm::APInt RightSideValue = RHSInt->getValue(); - if (LeftSideValue != 2 && LeftSideValue != 10) + // Is this s a macro from a system header? + if (S.Diags.getSuppressSystemWarnings() && S.SourceMgr.isInSystemMacro(Loc)) return; - if (LeftSideValue.getBitWidth() != RightSideValue.getBitWidth()) - return; + S.DiagRuntimeBehavior(Loc, Pointer, + S.PDiag(diag::warn_pointer_sub_null_ptr) + << S.getLangOpts().CPlusPlus + << Pointer->getSourceRange()); +} - CharSourceRange ExprRange = CharSourceRange::getCharRange( - LHSInt->getBeginLoc(), S.getLocForEndOfToken(RHSInt->getLocation())); - llvm::StringRef ExprStr = - Lexer::getSourceText(ExprRange, S.getSourceManager(), S.getLangOpts()); +/// Diagnose invalid arithmetic on two function pointers. +static void diagnoseArithmeticOnTwoFunctionPointers(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + assert(LHS->getType()->isAnyPointerType()); + assert(RHS->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 1 /* two pointers */ << LHS->getType()->getPointeeType() + // We only show the second type if it differs from the first. + << (unsigned)!S.Context.hasSameUnqualifiedType(LHS->getType(), + RHS->getType()) + << RHS->getType()->getPointeeType() + << LHS->getSourceRange() << RHS->getSourceRange(); +} - CharSourceRange XorRange = - CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); - llvm::StringRef XorStr = - Lexer::getSourceText(XorRange, S.getSourceManager(), S.getLangOpts()); - // Do not diagnose if xor keyword/macro is used. - if (XorStr == "xor") - return; +/// Diagnose invalid arithmetic on a function pointer. +static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + assert(Pointer->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 0 /* one pointer */ << Pointer->getType()->getPointeeType() + << 0 /* one pointer, so only one type */ + << Pointer->getSourceRange(); +} - std::string LHSStr = std::string(Lexer::getSourceText( - CharSourceRange::getTokenRange(LHSInt->getSourceRange()), - S.getSourceManager(), S.getLangOpts())); - std::string RHSStr = std::string(Lexer::getSourceText( - CharSourceRange::getTokenRange(RHSInt->getSourceRange()), - S.getSourceManager(), S.getLangOpts())); +/// Emit error if Operand is incomplete pointer type +/// +/// \returns True if pointer has incomplete type +static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, + Expr *Operand) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - if (Negative) { - RightSideValue = -RightSideValue; - RHSStr = "-" + RHSStr; - } else if (ExplicitPlus) { - RHSStr = "+" + RHSStr; - } + assert(ResType->isAnyPointerType()); + QualType PointeeTy = ResType->getPointeeType(); + return S.RequireCompleteSizedType( + Loc, PointeeTy, + diag::err_typecheck_arithmetic_incomplete_or_sizeless_type, + Operand->getSourceRange()); +} - StringRef LHSStrRef = LHSStr; - StringRef RHSStrRef = RHSStr; - // Do not diagnose literals with digit separators, binary, hexadecimal, octal - // literals. - if (LHSStrRef.starts_with("0b") || LHSStrRef.starts_with("0B") || - RHSStrRef.starts_with("0b") || RHSStrRef.starts_with("0B") || - LHSStrRef.starts_with("0x") || LHSStrRef.starts_with("0X") || - RHSStrRef.starts_with("0x") || RHSStrRef.starts_with("0X") || - (LHSStrRef.size() > 1 && LHSStrRef.starts_with("0")) || - (RHSStrRef.size() > 1 && RHSStrRef.starts_with("0")) || - LHSStrRef.contains('\'') || RHSStrRef.contains('\'')) +/* TO_UPSTREAM(BoundsSafety) ON*/ +// Looks at the expression and determine if it's a declaration that we +// could suggest adding `__counted_by` too. +// +// Returns a tuple of pointers that will be non null if +// the attribute should be suggested. +static std::tuple +shouldSuggestBoundsSafetyCountedBy(Expr *E) { + ValueDecl *decl = nullptr; + Expr *declRef = nullptr; + if (auto asDeclRefExpr = dyn_cast(E->IgnoreParenImpCasts())) { + declRef = asDeclRefExpr; + decl = asDeclRefExpr->getDecl(); + } else if (auto asMemberExpr = + dyn_cast(E->IgnoreParenImpCasts())) { + // Reference to a struct member + declRef = asMemberExpr; + decl = asMemberExpr->getMemberDecl(); + } + + if (declRef && declRef->getType()->isAtomicType()) { + // We don't suggest for atomic decls because they don't support the + // `__counted_by` attribute. + declRef = nullptr; + decl = nullptr; + } + return std::make_tuple(decl, declRef); +} + +static void emitBoundsSafetySinglePointerArithmeticError(Sema &S, Expr *Operand) { + // Try to find a declaration that we can suggest adding `__counted_by` too. + ValueDecl *decl = nullptr; + Expr *declRef = nullptr; + std::tie(decl, declRef) = shouldSuggestBoundsSafetyCountedBy(Operand); + + if (!declRef) { + // Didn't find a suitable a decl so emit error without suggesting the + // attribute. + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_single_pointer_arithmetic) + << Operand << /* %2 */ 0 << Operand->getSourceRange(); return; + } - bool SuggestXor = - S.getLangOpts().CPlusPlus || S.getPreprocessor().isMacroDefined("xor"); - const llvm::APInt XorValue = LeftSideValue ^ RightSideValue; - int64_t RightSideIntValue = RightSideValue.getSExtValue(); - if (LeftSideValue == 2 && RightSideIntValue >= 0) { - std::string SuggestedExpr = "1 << " + RHSStr; - bool Overflow = false; - llvm::APInt One = (LeftSideValue - 1); - llvm::APInt PowValue = One.sshl_ov(RightSideValue, Overflow); - if (Overflow) { - if (RightSideIntValue < 64) - S.Diag(Loc, diag::warn_xor_used_as_pow_base) - << ExprStr << toString(XorValue, 10, true) << ("1LL << " + RHSStr) - << FixItHint::CreateReplacement(ExprRange, "1LL << " + RHSStr); - else if (RightSideIntValue == 64) - S.Diag(Loc, diag::warn_xor_used_as_pow) - << ExprStr << toString(XorValue, 10, true); - else - return; - } else { - S.Diag(Loc, diag::warn_xor_used_as_pow_base_extra) - << ExprStr << toString(XorValue, 10, true) << SuggestedExpr - << toString(PowValue, 10, true) - << FixItHint::CreateReplacement( - ExprRange, (RightSideIntValue == 0) ? "1" : SuggestedExpr); - } + // Emit error suggesting the attribute. + assert(declRef->getType()->isPointerType()); + auto qualifiedName = decl->getQualifiedNameAsString(); + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_single_pointer_arithmetic) + << Operand << /* %2 */ 1 << qualifiedName << Operand->getSourceRange(); - S.Diag(Loc, diag::note_xor_used_as_pow_silence) - << ("0x2 ^ " + RHSStr) << SuggestXor; - } else if (LeftSideValue == 10) { - std::string SuggestedValue = "1e" + std::to_string(RightSideIntValue); - S.Diag(Loc, diag::warn_xor_used_as_pow_base) - << ExprStr << toString(XorValue, 10, true) << SuggestedValue - << FixItHint::CreateReplacement(ExprRange, SuggestedValue); - S.Diag(Loc, diag::note_xor_used_as_pow_silence) - << ("0xA ^ " + RHSStr) << SuggestXor; - } + // Emit note about where the decl is declared. + S.Diag(decl->getBeginLoc(), diag::note_pointer_declared_here_quoted) + << qualifiedName << decl->getSourceRange(); } -QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc) { - // Ensure that either both operands are of the same vector type, or - // one operand is of a vector type and the other is of its element type. - QualType vType = CheckVectorOperands(LHS, RHS, Loc, false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ false); - if (vType.isNull()) - return InvalidOperands(Loc, LHS, RHS); - if (getLangOpts().OpenCL && - getLangOpts().getOpenCLCompatibleVersion() < 120 && - vType->hasFloatingRepresentation()) - return InvalidOperands(Loc, LHS, RHS); - // FIXME: The check for C++ here is for GCC compatibility. GCC rejects the - // usage of the logical operators && and || with vectors in C. This - // check could be notionally dropped. - if (!getLangOpts().CPlusPlus && - !(isa(vType->getAs()))) - return InvalidLogicalVectorOperands(Loc, LHS, RHS); - - return GetSignedVectorType(LHS.get()->getType()); -} - -QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); +/// Check the validity of -fbounds-safety pointer arithmetic on increment/decrement. +static bool checkArithmeticUnaryOpBoundsSafetyPointer(Sema &S, Expr *Operand, bool IsInc) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - const MatrixType *LHSMatType = LHSType->getAs(); - const MatrixType *RHSMatType = RHSType->getAs(); - assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); + if (!ResType->isAnyPointerType()) + return true; - if (Context.hasSameType(LHSType, RHSType)) - return Context.getCommonSugaredType(LHSType, RHSType); + if (ResType->isBoundsAttributedType()) { + if (const auto *DCPTy = ResType->getAs()) { + if (!IsInc) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << DCPTy->getKind() << Operand->getSourceRange(); + return false; + } - // Type conversion may change LHS/RHS. Keep copies to the original results, in - // case we have to return InvalidOperands. - ExprResult OriginalLHS = LHS; - ExprResult OriginalRHS = RHS; - if (LHSMatType && !RHSMatType) { - RHS = tryConvertExprToType(RHS.get(), LHSMatType->getElementType()); - if (!RHS.isInvalid()) - return LHSType; + // Note when the new bounds check is off the check is handled in + // `CheckCountAttributedDeclAssignments::TraverseUnaryOperator`. + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + // TODO: When the diagnostic emitted by + // `BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp` + // is a hard error we can return false here. However, we can't do that + // right now because the diagnostic is a warning which can be ignored. + // So even if the a warning was issued we need to generate a valid AST. + S.BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + DCPTy, Operand, IsInc); + } - return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } else if (const auto *DRPTy = ResType->getAs()) { + if (!IsInc && !DRPTy->getStartPointer()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << /*ended_by*/ 5 << Operand->getSourceRange(); + return false; + } + if (IsInc && !DRPTy->getEndPointer()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << /*end*/ 4 << Operand->getSourceRange(); + return false; + } + } + return true; } - if (!LHSMatType && RHSMatType) { - LHS = tryConvertExprToType(LHS.get(), RHSMatType->getElementType()); - if (!LHS.isInvalid()) - return RHSType; - return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + if (ResType->isValueTerminatedType()) { + if (IsInc) + return true; + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_terminated_by_pointer_arithmetic_dec) + << Operand << Operand->getSourceRange(); + return false; } - return InvalidOperands(Loc, LHS, RHS); -} + if (ResType->isSinglePointerType()) { + emitBoundsSafetySinglePointerArithmeticError(S, Operand); + return false; + } -QualType Sema::CheckMatrixMultiplyOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); + if (auto *RD = ResType->getPointeeType()->getAs()) { + if (RD->getDecl()->hasFlexibleArrayMember()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_flexible_array_member_record_pointer_arithmetic); + return false; + } } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - auto *LHSMatType = LHS.get()->getType()->getAs(); - auto *RHSMatType = RHS.get()->getType()->getAs(); - assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); + if (!IsInc && ResType->isIndexablePointerType()) { + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_indexable_pointer_arithmetic) + << Operand << Operand->getSourceRange(); + return false; + } + return true; +} - if (LHSMatType && RHSMatType) { - if (LHSMatType->getNumColumns() != RHSMatType->getNumRows()) - return InvalidOperands(Loc, LHS, RHS); +/// Check the validity of -fbounds-safety pointer arithmetic. +static bool checkArithmeticBinOpBoundsSafetyPointer(Sema &S, Expr *Base, + Expr *Index, bool IndexNegated, + BinaryOperatorKind OpKind, + SourceLocation OpLoc) { + QualType BaseType = Base->getType(); + QualType IndexType = Index->getType(); + SourceLocation Loc = Base->getEndLoc(); - if (Context.hasSameType(LHSMatType, RHSMatType)) - return Context.getCommonSugaredType( - LHS.get()->getType().getUnqualifiedType(), - RHS.get()->getType().getUnqualifiedType()); + assert((!BaseType->isAtomicType() || + !BaseType->getAs()->getValueType()->isPointerType()) && + "Should have been checked before"); + assert(IndexType->isIntegerType() && "Should have been checked before"); - QualType LHSELTy = LHSMatType->getElementType(), - RHSELTy = RHSMatType->getElementType(); - if (!Context.hasSameType(LHSELTy, RHSELTy)) - return InvalidOperands(Loc, LHS, RHS); + auto PT = BaseType->getAs(); + if (!PT) + return true; - return Context.getConstantMatrixType( - Context.getCommonSugaredType(LHSELTy, RHSELTy), - LHSMatType->getNumRows(), RHSMatType->getNumColumns()); + if (BaseType->isValueTerminatedType()) { + Expr::EvalResult Result; + if (Index->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) { + llvm::APSInt index = Result.Val.getInt(); + if (IndexNegated) + index = -index; + if (index.isZero() || index.isOne()) + return true; + } + S.Diag(Loc, diag::err_bounds_safety_terminated_by_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; } - return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); -} -static bool isLegalBoolVectorBinaryOp(BinaryOperatorKind Opc) { - switch (Opc) { - default: + if (PT->isSingle() && !BaseType->isBoundsAttributedType()) { + emitBoundsSafetySinglePointerArithmeticError(S, Base); return false; - case BO_And: - case BO_AndAssign: - case BO_Or: - case BO_OrAssign: - case BO_Xor: - case BO_XorAssign: - return true; } -} -inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + // Note when the new bounds check is off the check is handled in + // `CheckCountAttributedDeclAssignments::TraverseBinaryOperator`. + if (const auto *CATTy = BaseType->getAs()) { + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + // TODO: When the diagnostic emitted by + // `BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp` + // is a hard error we can return false here. However, we can't do that + // right now because the diagnostic is a warning which can be ignored. So + // even if the a warning was issued we need to generate a valid AST. + S.BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, Base, OpKind); + } + } - bool IsCompAssign = - Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign; + if (auto *RD = PT->getPointeeType()->getAs()) { + if (RD->getDecl()->hasFlexibleArrayMember()) { + S.Diag(Base->getExprLoc(), + diag::err_bounds_safety_flexible_array_member_record_pointer_arithmetic); + return false; + } + } - bool LegalBoolVecOperator = isLegalBoolVectorBinaryOp(Opc); + Index = Index->IgnoreParenImpCasts(); + if (Index->isValueDependent()) + return true; - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ LegalBoolVecOperator, - /*ReportInvalid*/ true); - return InvalidOperands(Loc, LHS, RHS); - } + if (!PT->isIndexable()) + return true; - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_BitwiseOp); - return InvalidOperands(Loc, LHS, RHS); + if (IndexNegated) { + auto *RHSBInTy = dyn_cast(IndexType); + if (RHSBInTy && RHSBInTy->isUnsignedInteger()) { + S.Diag(Loc, diag::err_bounds_safety_indexable_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; + } } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_BitwiseOp); - return InvalidOperands(Loc, LHS, RHS); + Expr::EvalResult Result; + if (!Index->EvaluateAsInt(Result, S.getASTContext(), + Expr::SE_AllowSideEffects)) + // Render it to a runtime check. + return true; + + llvm::APSInt index = Result.Val.getInt(); + if (IndexNegated) + index = -index; + + if (index.isNegative()) { + S.Diag(Loc, diag::err_bounds_safety_indexable_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; } - if (Opc == BO_And) - diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ - if (LHS.get()->getType()->hasFloatingRepresentation() || - RHS.get()->getType()->hasFloatingRepresentation()) - return InvalidOperands(Loc, LHS, RHS); +/// Check the validity of an arithmetic pointer operand. +/// +/// If the operand has pointer type, this code will check for pointer types +/// which are invalid in arithmetic operations. These will be diagnosed +/// appropriately, including whether or not the use is supported as an +/// extension. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticOpPointerOperand(Sema &S, SourceLocation Loc, + Expr *Operand) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - ExprResult LHSResult = LHS, RHSResult = RHS; - QualType compType = UsualArithmeticConversions( - LHSResult, RHSResult, Loc, IsCompAssign ? ACK_CompAssign : ACK_BitwiseOp); - if (LHSResult.isInvalid() || RHSResult.isInvalid()) - return QualType(); - LHS = LHSResult.get(); - RHS = RHSResult.get(); + if (!ResType->isAnyPointerType()) return true; - if (Opc == BO_Xor) - diagnoseXorMisusedAsPow(*this, LHS, RHS, Loc); + QualType PointeeTy = ResType->getPointeeType(); + if (PointeeTy->isVoidType()) { + diagnoseArithmeticOnVoidPointer(S, Loc, Operand); + return !S.getLangOpts().CPlusPlus; + } + if (PointeeTy->isFunctionType()) { + diagnoseArithmeticOnFunctionPointer(S, Loc, Operand); + return !S.getLangOpts().CPlusPlus; + } - if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) - return compType; - return InvalidOperands(Loc, LHS, RHS); + if (checkArithmeticIncompletePointerType(S, Loc, Operand)) return false; + + return true; } -// C99 6.5.[13,14] -inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - // Check vector operands differently. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorLogicalOperands(LHS, RHS, Loc); +/// Check the validity of a binary arithmetic operation w.r.t. pointer +/// operands. +/// +/// This routine will diagnose any invalid arithmetic on pointer operands much +/// like \see checkArithmeticOpPointerOperand. However, it has special logic +/// for emitting a single diagnostic even for operations where both LHS and RHS +/// are (potentially problematic) pointers. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + bool isLHSPointer = LHSExpr->getType()->isAnyPointerType(); + bool isRHSPointer = RHSExpr->getType()->isAnyPointerType(); + if (!isLHSPointer && !isRHSPointer) return true; - bool EnumConstantInBoolContext = false; - for (const ExprResult &HS : {LHS, RHS}) { - if (const auto *DREHS = dyn_cast(HS.get())) { - const auto *ECDHS = dyn_cast(DREHS->getDecl()); - if (ECDHS && ECDHS->getInitVal() != 0 && ECDHS->getInitVal() != 1) - EnumConstantInBoolContext = true; + QualType LHSPointeeTy, RHSPointeeTy; + if (isLHSPointer) LHSPointeeTy = LHSExpr->getType()->getPointeeType(); + if (isRHSPointer) RHSPointeeTy = RHSExpr->getType()->getPointeeType(); + + // if both are pointers check if operation is valid wrt address spaces + if (isLHSPointer && isRHSPointer) { + if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy)) { + S.Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); + return false; } } - if (EnumConstantInBoolContext) - Diag(Loc, diag::warn_enum_constant_in_bool_context); + // Check for arithmetic on pointers to incomplete types. + bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); + bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); + if (isLHSVoidPtr || isRHSVoidPtr) { + if (!isRHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, LHSExpr); + else if (!isLHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, RHSExpr); + else diagnoseArithmeticOnTwoVoidPointers(S, Loc, LHSExpr, RHSExpr); - // WebAssembly tables can't be used with logical operators. - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - const auto *LHSATy = dyn_cast(LHSTy); - const auto *RHSATy = dyn_cast(RHSTy); - if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || - (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { - return InvalidOperands(Loc, LHS, RHS); + return !S.getLangOpts().CPlusPlus; } - // Diagnose cases where the user write a logical and/or but probably meant a - // bitwise one. We do this when the LHS is a non-bool integer and the RHS - // is a constant. - if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() && - !LHS.get()->getType()->isBooleanType() && - RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() && - // Don't warn in macros or template instantiations. - !Loc.isMacroID() && !inTemplateInstantiation()) { - // If the RHS can be constant folded, and if it constant folds to something - // that isn't 0 or 1 (which indicate a potential logical operation that - // happened to fold to true/false) then warn. - // Parens on the RHS are ignored. - Expr::EvalResult EVResult; - if (RHS.get()->EvaluateAsInt(EVResult, Context)) { - llvm::APSInt Result = EVResult.Val.getInt(); - if ((getLangOpts().CPlusPlus && !RHS.get()->getType()->isBooleanType() && - !RHS.get()->getExprLoc().isMacroID()) || - (Result != 0 && Result != 1)) { - Diag(Loc, diag::warn_logical_instead_of_bitwise) - << RHS.get()->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||"); - // Suggest replacing the logical operator with the bitwise version - Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator) - << (Opc == BO_LAnd ? "&" : "|") - << FixItHint::CreateReplacement( - SourceRange(Loc, getLocForEndOfToken(Loc)), - Opc == BO_LAnd ? "&" : "|"); - if (Opc == BO_LAnd) - // Suggest replacing "Foo() && kNonZero" with "Foo()" - Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant) - << FixItHint::CreateRemoval( - SourceRange(getLocForEndOfToken(LHS.get()->getEndLoc()), - RHS.get()->getEndLoc())); - } - } + bool isLHSFuncPtr = isLHSPointer && LHSPointeeTy->isFunctionType(); + bool isRHSFuncPtr = isRHSPointer && RHSPointeeTy->isFunctionType(); + if (isLHSFuncPtr || isRHSFuncPtr) { + if (!isRHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, LHSExpr); + else if (!isLHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, + RHSExpr); + else diagnoseArithmeticOnTwoFunctionPointers(S, Loc, LHSExpr, RHSExpr); + + return !S.getLangOpts().CPlusPlus; } - if (!Context.getLangOpts().CPlusPlus) { - // OpenCL v1.1 s6.3.g: The logical operators and (&&), or (||) do - // not operate on the built-in scalar and vector float types. - if (Context.getLangOpts().OpenCL && - Context.getLangOpts().OpenCLVersion < 120) { - if (LHS.get()->getType()->isFloatingType() || - RHS.get()->getType()->isFloatingType()) - return InvalidOperands(Loc, LHS, RHS); - } + if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) + return false; + if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) + return false; - LHS = UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); + return true; +} - RHS = UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) - return QualType(); +/// diagnoseStringPlusInt - Emit a warning when adding an integer to a string +/// literal. +static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + StringLiteral* StrExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); + Expr* IndexExpr = RHSExpr; + if (!StrExpr) { + StrExpr = dyn_cast(RHSExpr->IgnoreImpCasts()); + IndexExpr = LHSExpr; + } - if (!LHS.get()->getType()->isScalarType() || - !RHS.get()->getType()->isScalarType()) - return InvalidOperands(Loc, LHS, RHS); + bool IsStringPlusInt = StrExpr && + IndexExpr->getType()->isIntegralOrUnscopedEnumerationType(); + if (!IsStringPlusInt || IndexExpr->isValueDependent()) + return; - return Context.IntTy; - } + SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::warn_string_plus_int) + << DiagRange << IndexExpr->IgnoreImpCasts()->getType(); - // The following is safe because we only use this method for - // non-overloadable operands. + // Only print a fixit for "str" + int, not for int + "str". + if (IndexExpr == RHSExpr) { + SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) + << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") + << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") + << FixItHint::CreateInsertion(EndLoc, "]"); + } else + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); +} - // C++ [expr.log.and]p1 - // C++ [expr.log.or]p1 - // The operands are both contextually converted to type bool. - ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get()); - if (LHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); - LHS = LHSRes; +/// Emit a warning when adding a char literal to a string. +static void diagnoseStringPlusChar(Sema &Self, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + const Expr *StringRefExpr = LHSExpr; + const CharacterLiteral *CharExpr = + dyn_cast(RHSExpr->IgnoreImpCasts()); - ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); - if (RHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); - RHS = RHSRes; + if (!CharExpr) { + CharExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); + StringRefExpr = RHSExpr; + } - // C++ [expr.log.and]p2 - // C++ [expr.log.or]p2 - // The result is a bool. - return Context.BoolTy; -} + if (!CharExpr || !StringRefExpr) + return; -static bool IsReadonlyMessage(Expr *E, Sema &S) { - const MemberExpr *ME = dyn_cast(E); - if (!ME) return false; - if (!isa(ME->getMemberDecl())) return false; - ObjCMessageExpr *Base = dyn_cast( - ME->getBase()->IgnoreImplicit()->IgnoreParenImpCasts()); - if (!Base) return false; - return Base->getMethodDecl() != nullptr; -} + const QualType StringType = StringRefExpr->getType(); -/// Is the given expression (which must be 'const') a reference to a -/// variable which was originally non-const, but which has become -/// 'const' due to being captured within a block? -enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda }; -static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { - assert(E->isLValue() && E->getType().isConstQualified()); - E = E->IgnoreParens(); + // Return if not a PointerType. + if (!StringType->isAnyPointerType()) + return; - // Must be a reference to a declaration from an enclosing scope. - DeclRefExpr *DRE = dyn_cast(E); - if (!DRE) return NCCK_None; - if (!DRE->refersToEnclosingVariableOrCapture()) return NCCK_None; + // Return if not a CharacterType. + if (!StringType->getPointeeType()->isAnyCharacterType()) + return; - // The declaration must be a variable which is not declared 'const'. - VarDecl *var = dyn_cast(DRE->getDecl()); - if (!var) return NCCK_None; - if (var->getType().isConstQualified()) return NCCK_None; - assert(var->hasLocalStorage() && "capture added 'const' to non-local?"); + ASTContext &Ctx = Self.getASTContext(); + SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - // Decide whether the first capture was for a block or a lambda. - DeclContext *DC = S.CurContext, *Prev = nullptr; - // Decide whether the first capture was for a block or a lambda. - while (DC) { - // For init-capture, it is possible that the variable belongs to the - // template pattern of the current context. - if (auto *FD = dyn_cast(DC)) - if (var->isInitCapture() && - FD->getTemplateInstantiationPattern() == var->getDeclContext()) - break; - if (DC == var->getDeclContext()) - break; - Prev = DC; - DC = DC->getParent(); + const QualType CharType = CharExpr->getType(); + if (!CharType->isAnyCharacterType() && + CharType->isIntegerType() && + llvm::isUIntN(Ctx.getCharWidth(), CharExpr->getValue())) { + Self.Diag(OpLoc, diag::warn_string_plus_char) + << DiagRange << Ctx.CharTy; + } else { + Self.Diag(OpLoc, diag::warn_string_plus_char) + << DiagRange << CharExpr->getType(); } - // Unless we have an init-capture, we've gone one step too far. - if (!var->isInitCapture()) - DC = Prev; - return (isa(DC) ? NCCK_Block : NCCK_Lambda); -} -static bool IsTypeModifiable(QualType Ty, bool IsDereference) { - Ty = Ty.getNonReferenceType(); - if (IsDereference && Ty->isPointerType()) - Ty = Ty->getPointeeType(); - return !Ty.isConstQualified(); + // Only print a fixit for str + char, not for char + str. + if (isa(RHSExpr->IgnoreImpCasts())) { + SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) + << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") + << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") + << FixItHint::CreateInsertion(EndLoc, "]"); + } else { + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); + } } -// Update err_typecheck_assign_const and note_typecheck_assign_const -// when this enum is changed. -enum { - ConstFunction, - ConstVariable, - ConstMember, - ConstMethod, - NestedConstMember, - ConstUnknown, // Keep as last element -}; - -/// Emit the "read-only variable not assignable" error and print notes to give -/// more information about why the variable is not assignable, such as pointing -/// to the declaration of a const variable, showing that a method is const, or -/// that the function is returning a const reference. -static void DiagnoseConstAssignment(Sema &S, const Expr *E, - SourceLocation Loc) { - SourceRange ExprRange = E->getSourceRange(); +/// Emit error when two pointers are incompatible. +static void diagnosePointerIncompatibility(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + assert(LHSExpr->getType()->isAnyPointerType()); + assert(RHSExpr->getType()->isAnyPointerType()); + S.Diag(Loc, diag::err_typecheck_sub_ptr_compatible) + << LHSExpr->getType() << RHSExpr->getType() << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); +} - // Only emit one error on the first const found. All other consts will emit - // a note to the error. - bool DiagnosticEmitted = false; +// C99 6.5.6 +QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + QualType* CompLHSTy) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - // Track if the current expression is the result of a dereference, and if the - // next checked expression is the result of a dereference. - bool IsDereference = false; - bool NextIsDereference = false; + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } - // Loop to process MemberExpr chains. - while (true) { - IsDereference = NextIsDereference; + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + QualType compType = + CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; + } - E = E->IgnoreImplicit()->IgnoreParenImpCasts(); - if (const MemberExpr *ME = dyn_cast(E)) { - NextIsDereference = ME->isArrow(); - const ValueDecl *VD = ME->getMemberDecl(); - if (const FieldDecl *Field = dyn_cast(VD)) { - // Mutable fields can be modified even if the class is const. - if (Field->isMutable()) { - assert(DiagnosticEmitted && "Expected diagnostic not emitted."); - break; - } + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + QualType compType = + CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; + } - if (!IsTypeModifiable(Field->getType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstMember << false /*static*/ << Field - << Field->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstMember << false /*static*/ << Field << Field->getType() - << Field->getSourceRange(); - } - E = ME->getBase(); - continue; - } else if (const VarDecl *VDecl = dyn_cast(VD)) { - if (VDecl->getType().isConstQualified()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstMember << true /*static*/ << VDecl - << VDecl->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstMember << true /*static*/ << VDecl << VDecl->getType() - << VDecl->getSourceRange(); - } - // Static fields do not inherit constness from parents. - break; - } - break; // End MemberExpr - } else if (const ArraySubscriptExpr *ASE = - dyn_cast(E)) { - E = ASE->getBase()->IgnoreParenImpCasts(); - continue; - } else if (const ExtVectorElementExpr *EVE = - dyn_cast(E)) { - E = EVE->getBase()->IgnoreParenImpCasts(); - continue; - } - break; + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + + // Diagnose "string literal" '+' int and string '+' "char literal". + if (Opc == BO_Add) { + diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get()); + diagnoseStringPlusChar(*this, Loc, LHS.get(), RHS.get()); } - if (const CallExpr *CE = dyn_cast(E)) { - // Function calls - const FunctionDecl *FD = CE->getDirectCallee(); - if (FD && !IsTypeModifiable(FD->getReturnType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange - << ConstFunction << FD; - DiagnosticEmitted = true; - } - S.Diag(FD->getReturnTypeSourceRange().getBegin(), - diag::note_typecheck_assign_const) - << ConstFunction << FD << FD->getReturnType() - << FD->getReturnTypeSourceRange(); - } - } else if (const DeclRefExpr *DRE = dyn_cast(E)) { - // Point to variable declaration. - if (const ValueDecl *VD = DRE->getDecl()) { - if (!IsTypeModifiable(VD->getType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstVariable << VD << VD->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstVariable << VD << VD->getType() << VD->getSourceRange(); - } + // handle the common case first (both operands are arithmetic). + if (!compType.isNull() && compType->isArithmeticType()) { + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } + + // Type-checking. Ultimately the pointer's going to be in PExp; + // note that we bias towards the LHS being the pointer. + Expr *PExp = LHS.get(), *IExp = RHS.get(); + + bool isObjCPointer; + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + std::swap(PExp, IExp); + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + return InvalidOperands(Loc, LHS, RHS); } - } else if (isa(E)) { - if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { - if (const CXXMethodDecl *MD = dyn_cast(DC)) { - if (MD->isConst()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange - << ConstMethod << MD; - DiagnosticEmitted = true; - } - S.Diag(MD->getLocation(), diag::note_typecheck_assign_const) - << ConstMethod << MD << MD->getSourceRange(); - } - } + } + assert(PExp->getType()->isAnyPointerType()); + + if (!IExp->getType()->isIntegerType()) + return InvalidOperands(Loc, LHS, RHS); + + // Adding to a null pointer results in undefined behavior. + if (PExp->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull)) { + // In C++ adding zero to a null pointer is defined. + Expr::EvalResult KnownVal; + if (!getLangOpts().CPlusPlus || + (!IExp->isValueDependent() && + (!IExp->EvaluateAsInt(KnownVal, Context) || + KnownVal.Val.getInt() != 0))) { + // Check the conditions to see if this is the 'p = nullptr + n' idiom. + bool IsGNUIdiom = BinaryOperator::isNullPointerArithmeticExtension( + Context, BO_Add, PExp, IExp); + diagnoseArithmeticOnNullPointer(*this, Loc, PExp, IsGNUIdiom); } } - if (DiagnosticEmitted) - return; + if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) + return QualType(); - // Can't determine a more specific message, so display the generic error. - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown; -} + if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) + return QualType(); -enum OriginalExprKind { - OEK_Variable, - OEK_Member, - OEK_LValue -}; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && + !checkArithmeticBinOpBoundsSafetyPointer(*this, PExp, IExp, + /*IndexNegated*/ false, Opc, Loc)) + return QualType(); + // Cast __indexable to __bidi_indexable pointers for non-assignment + // addition. + if (Opc == BO_Add && PExp->getType()->isIndexablePointerType()) { + QualType Ty = Context.getBoundsSafetyPointerType( + PExp->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + ExprResult Res = ImpCastExprToType(PExp, Ty, CK_BoundsSafetyPointerCast); + if (PExp == LHS.get()) + LHS = Res; + else + RHS = Res; + PExp = Res.get(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ -static void DiagnoseRecursiveConstFields(Sema &S, const ValueDecl *VD, - const RecordType *Ty, - SourceLocation Loc, SourceRange Range, - OriginalExprKind OEK, - bool &DiagnosticEmitted) { - std::vector RecordTypeList; - RecordTypeList.push_back(Ty); - unsigned NextToCheckIndex = 0; - // We walk the record hierarchy breadth-first to ensure that we print - // diagnostics in field nesting order. - while (RecordTypeList.size() > NextToCheckIndex) { - bool IsNested = NextToCheckIndex > 0; - for (const FieldDecl *Field : - RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { - // First, check every field for constness. - QualType FieldTy = Field->getType(); - if (FieldTy.isConstQualified()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << Range << NestedConstMember << OEK << VD - << IsNested << Field; - DiagnosticEmitted = true; - } - S.Diag(Field->getLocation(), diag::note_typecheck_assign_const) - << NestedConstMember << IsNested << Field - << FieldTy << Field->getSourceRange(); - } + // Arithmetic on label addresses is normally allowed, except when we add + // a ptrauth signature to the addresses. + if (isa(PExp) && getLangOpts().PointerAuthIndirectGotos) { + Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) + << /*addition*/ 1; + return QualType(); + } - // Then we append it to the list to check next in order. - FieldTy = FieldTy.getCanonicalType(); - if (const auto *FieldRecTy = FieldTy->getAs()) { - if (!llvm::is_contained(RecordTypeList, FieldRecTy)) - RecordTypeList.push_back(FieldRecTy); - } + // Check array bounds for pointer arithemtic + CheckArrayAccess(PExp, IExp); + + if (CompLHSTy) { + QualType LHSTy = Context.isPromotableBitField(LHS.get()); + if (LHSTy.isNull()) { + LHSTy = LHS.get()->getType(); + if (Context.isPromotableIntegerType(LHSTy)) + LHSTy = Context.getPromotedIntegerType(LHSTy); } - ++NextToCheckIndex; + *CompLHSTy = LHSTy; } + + return PExp->getType(); } -/// Emit an error for the case where a record we are trying to assign to has a -/// const-qualified field somewhere in its hierarchy. -static void DiagnoseRecursiveConstFields(Sema &S, const Expr *E, - SourceLocation Loc) { - QualType Ty = E->getType(); - assert(Ty->isRecordType() && "lvalue was not record?"); - SourceRange Range = E->getSourceRange(); - const RecordType *RTy = Ty.getCanonicalType()->getAs(); - bool DiagEmitted = false; +// C99 6.5.6 +QualType Sema::CheckSubtractionOperands( + ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, + /*TO_UPSTREAM(BoundsSafety) ON*/ + BinaryOperatorKind Opc, + /*TO_UPSTREAM(BoundsSafety) OFF*/ + QualType *CompLHSTy) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - if (const MemberExpr *ME = dyn_cast(E)) - DiagnoseRecursiveConstFields(S, ME->getMemberDecl(), RTy, Loc, - Range, OEK_Member, DiagEmitted); - else if (const DeclRefExpr *DRE = dyn_cast(E)) - DiagnoseRecursiveConstFields(S, DRE->getDecl(), RTy, Loc, - Range, OEK_Variable, DiagEmitted); - else - DiagnoseRecursiveConstFields(S, nullptr, RTy, Loc, - Range, OEK_LValue, DiagEmitted); - if (!DiagEmitted) - DiagnoseConstAssignment(S, E, Loc); -} + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } -/// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, -/// emit an error and return true. If so, return false. -static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { - assert(!E->hasPlaceholderType(BuiltinType::PseudoObject)); + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + QualType compType = + CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; + } - S.CheckShadowingDeclModification(E, Loc); + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + QualType compType = + CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; + } - SourceLocation OrigLoc = Loc; - Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, - &Loc); - if (IsLV == Expr::MLV_ClassTemporary && IsReadonlyMessage(E, S)) - IsLV = Expr::MLV_InvalidMessageExpression; - if (IsLV == Expr::MLV_Valid) - return false; + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); - unsigned DiagID = 0; - bool NeedType = false; - switch (IsLV) { // C99 6.5.16p2 - case Expr::MLV_ConstQualified: - // Use a specialized diagnostic when we're assigning to an object - // from an enclosing function or block. - if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) { - if (NCCK == NCCK_Block) - DiagID = diag::err_block_decl_ref_not_modifiable_lvalue; - else - DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue; - break; - } + // Enforce type constraints: C99 6.5.6p3. - // In ARC, use some specialized diagnostics for occasions where we - // infer 'const'. These are always pseudo-strong variables. - if (S.getLangOpts().ObjCAutoRefCount) { - DeclRefExpr *declRef = dyn_cast(E->IgnoreParenCasts()); - if (declRef && isa(declRef->getDecl())) { - VarDecl *var = cast(declRef->getDecl()); + // Handle the common case first (both operands are arithmetic). + if (!compType.isNull() && compType->isArithmeticType()) { + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } - // Use the normal diagnostic if it's pseudo-__strong but the - // user actually wrote 'const'. - if (var->isARCPseudoStrong() && - (!var->getTypeSourceInfo() || - !var->getTypeSourceInfo()->getType().isConstQualified())) { - // There are three pseudo-strong cases: - // - self - ObjCMethodDecl *method = S.getCurMethodDecl(); - if (method && var == method->getSelfDecl()) { - DiagID = method->isClassMethod() - ? diag::err_typecheck_arc_assign_self_class_method - : diag::err_typecheck_arc_assign_self; + // Either ptr - int or ptr - ptr. + if (LHS.get()->getType()->isAnyPointerType()) { + QualType lpointee = LHS.get()->getType()->getPointeeType(); - // - Objective-C externally_retained attribute. - } else if (var->hasAttr() || - isa(var)) { - DiagID = diag::err_typecheck_arc_assign_externally_retained; + // Diagnose bad cases where we step over interface counts. + if (LHS.get()->getType()->isObjCObjectPointerType() && + checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) + return QualType(); - // - fast enumeration variables - } else { - DiagID = diag::err_typecheck_arr_assign_enumeration; - } + // Arithmetic on label addresses is normally allowed, except when we add + // a ptrauth signature to the addresses. + if (isa(LHS.get()) && + getLangOpts().PointerAuthIndirectGotos) { + Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) + << /*subtraction*/ 0; + return QualType(); + } - SourceRange Assign; - if (Loc != OrigLoc) - Assign = SourceRange(OrigLoc, OrigLoc); - S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; - // We need to preserve the AST regardless, so migration tool - // can do its job. - return false; + // The result type of a pointer-int computation is the pointer type. + if (RHS.get()->getType()->isIntegerType()) { + // Subtracting from a null pointer should produce a warning. + // The last argument to the diagnose call says this doesn't match the + // GNU int-to-pointer idiom. + if (LHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull)) { + // In C++ adding zero to a null pointer is defined. + Expr::EvalResult KnownVal; + if (!getLangOpts().CPlusPlus || + (!RHS.get()->isValueDependent() && + (!RHS.get()->EvaluateAsInt(KnownVal, Context) || + KnownVal.Val.getInt() != 0))) { + diagnoseArithmeticOnNullPointer(*this, Loc, LHS.get(), false); } } - } - // If none of the special cases above are triggered, then this is a - // simple const assignment. - if (DiagID == 0) { - DiagnoseConstAssignment(S, E, Loc); - return true; - } + if (!checkArithmeticOpPointerOperand(*this, Loc, LHS.get())) + return QualType(); - break; - case Expr::MLV_ConstAddrSpace: - DiagnoseConstAssignment(S, E, Loc); - return true; - case Expr::MLV_ConstQualifiedField: - DiagnoseRecursiveConstFields(S, E, Loc); - return true; - case Expr::MLV_ArrayType: - case Expr::MLV_ArrayTemporary: - DiagID = diag::err_typecheck_array_not_modifiable_lvalue; - NeedType = true; - break; - case Expr::MLV_NotObjectType: - DiagID = diag::err_typecheck_non_object_not_modifiable_lvalue; - NeedType = true; - break; - case Expr::MLV_LValueCast: - DiagID = diag::err_typecheck_lvalue_casts_not_supported; - break; - case Expr::MLV_Valid: - llvm_unreachable("did not take early return for MLV_Valid"); - case Expr::MLV_InvalidExpression: - case Expr::MLV_MemberFunction: - case Expr::MLV_ClassTemporary: - DiagID = diag::err_typecheck_expression_not_modifiable_lvalue; - break; - case Expr::MLV_IncompleteType: - case Expr::MLV_IncompleteVoidType: - return S.RequireCompleteType(Loc, E->getType(), - diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); - case Expr::MLV_DuplicateVectorComponents: - DiagID = diag::err_typecheck_duplicate_vector_components_not_mlvalue; - break; - case Expr::MLV_NoSetterProperty: - llvm_unreachable("readonly properties should be processed differently"); - case Expr::MLV_InvalidMessageExpression: - DiagID = diag::err_readonly_message_assignment; - break; - case Expr::MLV_SubObjCPropertySetting: - DiagID = diag::err_no_subobject_property_setting; - break; - } + // Check array bounds for pointer arithemtic + CheckArrayAccess(LHS.get(), RHS.get(), /*ArraySubscriptExpr*/nullptr, + /*AllowOnePastEnd*/true, /*IndexNegated*/true); - SourceRange Assign; - if (Loc != OrigLoc) - Assign = SourceRange(OrigLoc, OrigLoc); - if (NeedType) - S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign; - else - S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; - return true; -} + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !checkArithmeticBinOpBoundsSafetyPointer( + *this, LHS.get(), RHS.get(), + /*IndexNegated*/ true, Opc, Loc)) + return QualType(); + // If the pointer in pointer-int substractions is __indexable, cast it + // to __bidi_indexable. Ignore pointer-pointer substractions here. + bool IsSubAssign = CompLHSTy; + if (!IsSubAssign && LHS.get()->getType()->isIndexablePointerType() && + !RHS.get()->getType()->isPointerType()) { + QualType Ty = Context.getBoundsSafetyPointerType( + LHS.get()->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + LHS = ImpCastExprToType(LHS.get(), Ty, CK_BoundsSafetyPointerCast); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ -static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr, - SourceLocation Loc, - Sema &Sema) { - if (Sema.inTemplateInstantiation()) - return; - if (Sema.isUnevaluatedContext()) - return; - if (Loc.isInvalid() || Loc.isMacroID()) - return; - if (LHSExpr->getExprLoc().isMacroID() || RHSExpr->getExprLoc().isMacroID()) - return; + if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); + return LHS.get()->getType(); + } - // C / C++ fields - MemberExpr *ML = dyn_cast(LHSExpr); - MemberExpr *MR = dyn_cast(RHSExpr); - if (ML && MR) { - if (!(isa(ML->getBase()) && isa(MR->getBase()))) - return; - const ValueDecl *LHSDecl = - cast(ML->getMemberDecl()->getCanonicalDecl()); - const ValueDecl *RHSDecl = - cast(MR->getMemberDecl()->getCanonicalDecl()); - if (LHSDecl != RHSDecl) - return; - if (LHSDecl->getType().isVolatileQualified()) - return; - if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) - if (RefTy->getPointeeType().isVolatileQualified()) - return; + // Handle pointer-pointer subtractions. + if (const PointerType *RHSPTy + = RHS.get()->getType()->getAs()) { + QualType rpointee = RHSPTy->getPointeeType(); - Sema.Diag(Loc, diag::warn_identity_field_assign) << 0; - } + if (getLangOpts().CPlusPlus) { + // Pointee types must be the same: C++ [expr.add] + if (!Context.hasSameUnqualifiedType(lpointee, rpointee)) { + diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); + } + } else { + // Pointee types must be compatible C99 6.5.6p3 + if (!Context.typesAreCompatible( + Context.getCanonicalType(lpointee).getUnqualifiedType(), + Context.getCanonicalType(rpointee).getUnqualifiedType())) { + diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); + return QualType(); + } + } - // Objective-C instance variables - ObjCIvarRefExpr *OL = dyn_cast(LHSExpr); - ObjCIvarRefExpr *OR = dyn_cast(RHSExpr); - if (OL && OR && OL->getDecl() == OR->getDecl()) { - DeclRefExpr *RL = dyn_cast(OL->getBase()->IgnoreImpCasts()); - DeclRefExpr *RR = dyn_cast(OR->getBase()->IgnoreImpCasts()); - if (RL && RR && RL->getDecl() == RR->getDecl()) - Sema.Diag(Loc, diag::warn_identity_field_assign) << 1; - } -} - -// C99 6.5.16.1 -QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, - SourceLocation Loc, - QualType CompoundType, - BinaryOperatorKind Opc) { - assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)); - - // Verify that LHS is a modifiable lvalue, and emit error if not. - if (CheckForModifiableLvalue(LHSExpr, Loc, *this)) - return QualType(); - - QualType LHSType = LHSExpr->getType(); - QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() : - CompoundType; - // OpenCL v1.2 s6.1.1.1 p2: - // The half data type can only be used to declare a pointer to a buffer that - // contains half values - if (getLangOpts().OpenCL && - !getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()) && - LHSType->isHalfType()) { - Diag(Loc, diag::err_opencl_half_load_store) << 1 - << LHSType.getUnqualifiedType(); - return QualType(); - } - - // WebAssembly tables can't be used on RHS of an assignment expression. - if (RHSType->isWebAssemblyTableType()) { - Diag(Loc, diag::err_wasm_table_art) << 0; - return QualType(); - } - - AssignConvertType ConvTy; - if (CompoundType.isNull()) { - Expr *RHSCheck = RHS.get(); - - CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this); - - QualType LHSTy(LHSType); - ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS); - if (RHS.isInvalid()) - return QualType(); - // Special case of NSObject attributes on c-style pointer types. - if (ConvTy == IncompatiblePointer && - ((Context.isObjCNSObjectType(LHSType) && - RHSType->isObjCObjectPointerType()) || - (Context.isObjCNSObjectType(RHSType) && - LHSType->isObjCObjectPointerType()))) - ConvTy = Compatible; - - if (ConvTy == Compatible && - LHSType->isObjCObjectType()) - Diag(Loc, diag::err_objc_object_assignment) - << LHSType; - - // If the RHS is a unary plus or minus, check to see if they = and + are - // right next to each other. If so, the user may have typo'd "x =+ 4" - // instead of "x += 4". - if (ImplicitCastExpr *ICE = dyn_cast(RHSCheck)) - RHSCheck = ICE->getSubExpr(); - if (UnaryOperator *UO = dyn_cast(RHSCheck)) { - if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) && - Loc.isFileID() && UO->getOperatorLoc().isFileID() && - // Only if the two operators are exactly adjacent. - Loc.getLocWithOffset(1) == UO->getOperatorLoc() && - // And there is a space or other character before the subexpr of the - // unary +/-. We don't want to warn on "x=-1". - Loc.getLocWithOffset(2) != UO->getSubExpr()->getBeginLoc() && - UO->getSubExpr()->getBeginLoc().isFileID()) { - Diag(Loc, diag::warn_not_compound_assign) - << (UO->getOpcode() == UO_Plus ? "+" : "-") - << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); - } - } + if (!checkArithmeticBinOpPointerOperands(*this, Loc, + LHS.get(), RHS.get())) + return QualType(); - if (ConvTy == Compatible) { - if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) { - // Warn about retain cycles where a block captures the LHS, but - // not if the LHS is a simple variable into which the block is - // being stored...unless that variable can be captured by reference! - const Expr *InnerLHS = LHSExpr->IgnoreParenCasts(); - const DeclRefExpr *DRE = dyn_cast(InnerLHS); - if (!DRE || DRE->getDecl()->hasAttr()) - ObjC().checkRetainCycles(LHSExpr, RHS.get()); - } + bool LHSIsNullPtr = LHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + bool RHSIsNullPtr = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); - if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong || - LHSType.isNonWeakInMRRWithObjCWeak(Context)) { - // It is safe to assign a weak reference into a strong variable. - // Although this code can still have problems: - // id x = self.weakProp; - // id y = self.weakProp; - // we do not warn to warn spuriously when 'x' and 'y' are on separate - // paths through the function. This should be revisited if - // -Wrepeated-use-of-weak is made flow-sensitive. - // For ObjCWeak only, we do not warn if the assign is to a non-weak - // variable, which will be valid for the current autorelease scope. - if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, - RHS.get()->getBeginLoc())) - getCurFunction()->markSafeWeakUse(RHS.get()); + // Subtracting nullptr or from nullptr is suspect + if (LHSIsNullPtr) + diagnoseSubtractionOnNullPointer(*this, Loc, LHS.get(), RHSIsNullPtr); + if (RHSIsNullPtr) + diagnoseSubtractionOnNullPointer(*this, Loc, RHS.get(), LHSIsNullPtr); - } else if (getLangOpts().ObjCAutoRefCount || getLangOpts().ObjCWeak) { - checkUnsafeExprAssigns(Loc, LHSExpr, RHS.get()); + // The pointee type may have zero size. As an extension, a structure or + // union may have zero size or an array may have zero length. In this + // case subtraction does not make sense. + if (!rpointee->isVoidType() && !rpointee->isFunctionType()) { + CharUnits ElementSize = Context.getTypeSizeInChars(rpointee); + if (ElementSize.isZero()) { + Diag(Loc,diag::warn_sub_ptr_zero_size_types) + << rpointee.getUnqualifiedType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } } - } - } else { - // Compound assignment "x += y" - ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); - } - - if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, - RHS.get(), AA_Assigning)) - return QualType(); - - CheckForNullPointerDereference(*this, LHSExpr); - AssignedEntity AE{LHSExpr}; - checkExprLifetime(*this, AE, RHS.get()); - - if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) { - if (CompoundType.isNull()) { - // C++2a [expr.ass]p5: - // A simple-assignment whose left operand is of a volatile-qualified - // type is deprecated unless the assignment is either a discarded-value - // expression or an unevaluated operand - ExprEvalContexts.back().VolatileAssignmentLHSs.push_back(LHSExpr); + if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); + return Context.getPointerDiffType(); } } - // C11 6.5.16p3: The type of an assignment expression is the type of the - // left operand would have after lvalue conversion. - // C11 6.3.2.1p2: ...this is called lvalue conversion. If the lvalue has - // qualified type, the value has the unqualified version of the type of the - // lvalue; additionally, if the lvalue has atomic type, the value has the - // non-atomic version of the type of the lvalue. - // C++ 5.17p1: the type of the assignment expression is that of its left - // operand. - return getLangOpts().CPlusPlus ? LHSType : LHSType.getAtomicUnqualifiedType(); + return InvalidOperands(Loc, LHS, RHS); } -// Scenarios to ignore if expression E is: -// 1. an explicit cast expression into void -// 2. a function call expression that returns void -static bool IgnoreCommaOperand(const Expr *E, const ASTContext &Context) { - E = E->IgnoreParens(); - - if (const CastExpr *CE = dyn_cast(E)) { - if (CE->getCastKind() == CK_ToVoid) { - return true; - } - - // static_cast on a dependent type will not show up as CK_ToVoid. - if (CE->getCastKind() == CK_Dependent && E->getType()->isVoidType() && - CE->getSubExpr()->getType()->isDependentType()) { - return true; - } - } - - if (const auto *CE = dyn_cast(E)) - return CE->getCallReturnType(Context)->isVoidType(); +static bool isScopedEnumerationType(QualType T) { + if (const EnumType *ET = T->getAs()) + return ET->getDecl()->isScoped(); return false; } -void Sema::DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc) { - // No warnings in macros - if (Loc.isMacroID()) +static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + QualType LHSType) { + // OpenCL 6.3j: shift values are effectively % word size of LHS (more defined), + // so skip remaining warnings as we don't want to modify values within Sema. + if (S.getLangOpts().OpenCL) return; - // Don't warn in template instantiations. - if (inTemplateInstantiation()) + // Check right/shifter operand + Expr::EvalResult RHSResult; + if (RHS.get()->isValueDependent() || + !RHS.get()->EvaluateAsInt(RHSResult, S.Context)) return; + llvm::APSInt Right = RHSResult.Val.getInt(); - // Scope isn't fine-grained enough to explicitly list the specific cases, so - // instead, skip more than needed, then call back into here with the - // CommaVisitor in SemaStmt.cpp. - // The listed locations are the initialization and increment portions - // of a for loop. The additional checks are on the condition of - // if statements, do/while loops, and for loops. - // Differences in scope flags for C89 mode requires the extra logic. - const unsigned ForIncrementFlags = - getLangOpts().C99 || getLangOpts().CPlusPlus - ? Scope::ControlScope | Scope::ContinueScope | Scope::BreakScope - : Scope::ContinueScope | Scope::BreakScope; - const unsigned ForInitFlags = Scope::ControlScope | Scope::DeclScope; - const unsigned ScopeFlags = getCurScope()->getFlags(); - if ((ScopeFlags & ForIncrementFlags) == ForIncrementFlags || - (ScopeFlags & ForInitFlags) == ForInitFlags) + if (Right.isNegative()) { + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_shift_negative) + << RHS.get()->getSourceRange()); return; - - // If there are multiple comma operators used together, get the RHS of the - // of the comma operator as the LHS. - while (const BinaryOperator *BO = dyn_cast(LHS)) { - if (BO->getOpcode() != BO_Comma) - break; - LHS = BO->getRHS(); } - // Only allow some expressions on LHS to not warn. - if (IgnoreCommaOperand(LHS, Context)) + QualType LHSExprType = LHS.get()->getType(); + uint64_t LeftSize = S.Context.getTypeSize(LHSExprType); + if (LHSExprType->isBitIntType()) + LeftSize = S.Context.getIntWidth(LHSExprType); + else if (LHSExprType->isFixedPointType()) { + auto FXSema = S.Context.getFixedPointSemantics(LHSExprType); + LeftSize = FXSema.getWidth() - (unsigned)FXSema.hasUnsignedPadding(); + } + if (Right.uge(LeftSize)) { + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_shift_gt_typewidth) + << RHS.get()->getSourceRange()); return; + } - Diag(Loc, diag::warn_comma_operator); - Diag(LHS->getBeginLoc(), diag::note_cast_to_void) - << LHS->getSourceRange() - << FixItHint::CreateInsertion(LHS->getBeginLoc(), - LangOpts.CPlusPlus ? "static_cast(" - : "(void)(") - << FixItHint::CreateInsertion(PP.getLocForEndOfToken(LHS->getEndLoc()), - ")"); -} + // FIXME: We probably need to handle fixed point types specially here. + if (Opc != BO_Shl || LHSExprType->isFixedPointType()) + return; -// C99 6.5.17 -static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc) { - LHS = S.CheckPlaceholderExpr(LHS.get()); - RHS = S.CheckPlaceholderExpr(RHS.get()); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // When left shifting an ICE which is signed, we can check for overflow which + // according to C++ standards prior to C++2a has undefined behavior + // ([expr.shift] 5.8/2). Unsigned integers have defined behavior modulo one + // more than the maximum value representable in the result type, so never + // warn for those. (FIXME: Unsigned left-shift overflow in a constant + // expression is still probably a bug.) + Expr::EvalResult LHSResult; + if (LHS.get()->isValueDependent() || + LHSType->hasUnsignedIntegerRepresentation() || + !LHS.get()->EvaluateAsInt(LHSResult, S.Context)) + return; + llvm::APSInt Left = LHSResult.Val.getInt(); - // C's comma performs lvalue conversion (C99 6.3.2.1) on both its - // operands, but not unary promotions. - // C++'s comma does not do any conversions at all (C++ [expr.comma]p1). + // Don't warn if signed overflow is defined, then all the rest of the + // diagnostics will not be triggered because the behavior is defined. + // Also don't warn in C++20 mode (and newer), as signed left shifts + // always wrap and never overflow. + if (S.getLangOpts().isSignedOverflowDefined() || S.getLangOpts().CPlusPlus20) + return; - // So we treat the LHS as a ignored value, and in C++ we allow the - // containing site to determine what should be done with the RHS. - LHS = S.IgnoredValueConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); + // If LHS does not have a non-negative value then, the + // behavior is undefined before C++2a. Warn about it. + if (Left.isNegative()) { + S.DiagRuntimeBehavior(Loc, LHS.get(), + S.PDiag(diag::warn_shift_lhs_negative) + << LHS.get()->getSourceRange()); + return; + } - S.DiagnoseUnusedExprResult(LHS.get(), diag::warn_unused_comma_left_operand); + llvm::APInt ResultBits = + static_cast(Right) + Left.getSignificantBits(); + if (ResultBits.ule(LeftSize)) + return; + llvm::APSInt Result = Left.extend(ResultBits.getLimitedValue()); + Result = Result.shl(Right); - if (!S.getLangOpts().CPlusPlus) { - RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - if (!RHS.get()->getType()->isVoidType()) - S.RequireCompleteType(Loc, RHS.get()->getType(), - diag::err_incomplete_type); - } + // Print the bit representation of the signed integer as an unsigned + // hexadecimal number. + SmallString<40> HexResult; + Result.toString(HexResult, 16, /*Signed =*/false, /*Literal =*/true); - if (!S.getDiagnostics().isIgnored(diag::warn_comma_operator, Loc)) - S.DiagnoseCommaOperator(LHS.get(), Loc); + // If we are only missing a sign bit, this is less likely to result in actual + // bugs -- if the result is cast back to an unsigned type, it will have the + // expected value. Thus we place this behind a different warning that can be + // turned off separately if needed. + if (ResultBits - 1 == LeftSize) { + S.Diag(Loc, diag::warn_shift_result_sets_sign_bit) + << HexResult << LHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return; + } - return RHS.get()->getType(); + S.Diag(Loc, diag::warn_shift_result_gt_typewidth) + << HexResult.str() << Result.getSignificantBits() << LHSType + << Left.getBitWidth() << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); } -/// CheckIncrementDecrementOperand - unlike most "Check" methods, this routine -/// doesn't need to call UsualUnaryConversions or UsualArithmeticConversions. -static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, - ExprValueKind &VK, - ExprObjectKind &OK, - SourceLocation OpLoc, bool IsInc, - bool IsPrefix) { - QualType ResType = Op->getType(); - // Atomic types can be used for increment / decrement where the non-atomic - // versions can, so ignore the _Atomic() specifier for the purpose of - // checking. - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); +/// Return the resulting type when a vector is shifted +/// by a scalar or vector shift amount. +static QualType checkVectorShift(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompAssign) { + // OpenCL v1.1 s6.3.j says RHS can be a vector only if LHS is a vector. + if ((S.LangOpts.OpenCL || S.LangOpts.ZVector) && + !LHS.get()->getType()->isVectorType()) { + S.Diag(Loc, diag::err_shift_rhs_only_vector) + << RHS.get()->getType() << LHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - assert(!ResType.isNull() && "no type for increment/decrement expression"); + if (!IsCompAssign) { + LHS = S.UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) return QualType(); + } - if (S.getLangOpts().CPlusPlus && ResType->isBooleanType()) { - // Decrement of bool is not allowed. - if (!IsInc) { - S.Diag(OpLoc, diag::err_decrement_bool) << Op->getSourceRange(); - return QualType(); - } - // Increment of bool sets it to true, but is deprecated. - S.Diag(OpLoc, S.getLangOpts().CPlusPlus17 ? diag::ext_increment_bool - : diag::warn_increment_bool) - << Op->getSourceRange(); - } else if (S.getLangOpts().CPlusPlus && ResType->isEnumeralType()) { - // Error on enum increments and decrements in C++ mode - S.Diag(OpLoc, diag::err_increment_decrement_enum) << IsInc << ResType; + RHS = S.UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) return QualType(); + + QualType LHSType = LHS.get()->getType(); + // Note that LHS might be a scalar because the routine calls not only in + // OpenCL case. + const VectorType *LHSVecTy = LHSType->getAs(); + QualType LHSEleType = LHSVecTy ? LHSVecTy->getElementType() : LHSType; + + // Note that RHS might not be a vector. + QualType RHSType = RHS.get()->getType(); + const VectorType *RHSVecTy = RHSType->getAs(); + QualType RHSEleType = RHSVecTy ? RHSVecTy->getElementType() : RHSType; + + // Do not allow shifts for boolean vectors. + if ((LHSVecTy && LHSVecTy->isExtVectorBoolType()) || + (RHSVecTy && RHSVecTy->isExtVectorBoolType())) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange(); return QualType(); - } else if (ResType->isRealType()) { - // OK! - } else if (ResType->isPointerType()) { - // C99 6.5.2.4p2, 6.5.6p2 - if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) - return QualType(); - } else if (ResType->isObjCObjectPointerType()) { - // On modern runtimes, ObjC pointer arithmetic is forbidden. - // Otherwise, we just need a complete type. - if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || - checkArithmeticOnObjCPointer(S, OpLoc, Op)) - return QualType(); - } else if (ResType->isAnyComplexType()) { - // C99 does not support ++/-- on complex types, we allow as an extension. - S.Diag(OpLoc, S.getLangOpts().C2y ? diag::warn_c2y_compat_increment_complex - : diag::ext_c2y_increment_complex) - << IsInc << Op->getSourceRange(); - } else if (ResType->isPlaceholderType()) { - ExprResult PR = S.CheckPlaceholderExpr(Op); - if (PR.isInvalid()) return QualType(); - return CheckIncrementDecrementOperand(S, PR.get(), VK, OK, OpLoc, - IsInc, IsPrefix); - } else if (S.getLangOpts().AltiVec && ResType->isVectorType()) { - // OK! ( C/C++ Language Extensions for CBEA(Version 2.6) 10.3 ) - } else if (S.getLangOpts().ZVector && ResType->isVectorType() && - (ResType->castAs()->getVectorKind() != - VectorKind::AltiVecBool)) { - // The z vector extensions allow ++ and -- for non-bool vectors. - } else if (S.getLangOpts().OpenCL && ResType->isVectorType() && - ResType->castAs()->getElementType()->isIntegerType()) { - // OpenCL V1.2 6.3 says dec/inc ops operate on integer vector types. - } else { - S.Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement) - << ResType << int(IsInc) << Op->getSourceRange(); + } + + // The operands need to be integers. + if (!LHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << LHS.get()->getType() << LHS.get()->getSourceRange(); return QualType(); } - // At this point, we know we have a real, complex or pointer type. - // Now make sure the operand is a modifiable lvalue. - if (CheckForModifiableLvalue(Op, OpLoc, S)) + + if (!RHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << RHS.get()->getType() << RHS.get()->getSourceRange(); return QualType(); - if (S.getLangOpts().CPlusPlus20 && ResType.isVolatileQualified()) { - // C++2a [expr.pre.inc]p1, [expr.post.inc]p1: - // An operand with volatile-qualified type is deprecated - S.Diag(OpLoc, diag::warn_deprecated_increment_decrement_volatile) - << IsInc << ResType; } - // In C++, a prefix increment is the same type as the operand. Otherwise - // (in C or with postfix), the increment is the unqualified type of the - // operand. - if (IsPrefix && S.getLangOpts().CPlusPlus) { - VK = VK_LValue; - OK = Op->getObjectKind(); - return ResType; + + if (!LHSVecTy) { + assert(RHSVecTy); + if (IsCompAssign) + return RHSType; + if (LHSEleType != RHSEleType) { + LHS = S.ImpCastExprToType(LHS.get(),RHSEleType, CK_IntegralCast); + LHSEleType = RHSEleType; + } + QualType VecTy = + S.Context.getExtVectorType(LHSEleType, RHSVecTy->getNumElements()); + LHS = S.ImpCastExprToType(LHS.get(), VecTy, CK_VectorSplat); + LHSType = VecTy; + } else if (RHSVecTy) { + // OpenCL v1.1 s6.3.j says that for vector types, the operators + // are applied component-wise. So if RHS is a vector, then ensure + // that the number of elements is the same as LHS... + if (RHSVecTy->getNumElements() != LHSVecTy->getNumElements()) { + S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } + if (!S.LangOpts.OpenCL && !S.LangOpts.ZVector) { + const BuiltinType *LHSBT = LHSEleType->getAs(); + const BuiltinType *RHSBT = RHSEleType->getAs(); + if (LHSBT != RHSBT && + S.Context.getTypeSize(LHSBT) != S.Context.getTypeSize(RHSBT)) { + S.Diag(Loc, diag::warn_typecheck_vector_element_sizes_not_equal) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } + } } else { - VK = VK_PRValue; - return ResType.getUnqualifiedType(); + // ...else expand RHS to match the number of elements in LHS. + QualType VecTy = + S.Context.getExtVectorType(RHSEleType, LHSVecTy->getNumElements()); + RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); } + + return LHSType; } -/// getPrimaryDecl - Helper function for CheckAddressOfOperand(). -/// This routine allows us to typecheck complex/recursive expressions -/// where the declaration is needed for type checking. We only need to -/// handle cases when the expression references a function designator -/// or is an lvalue. Here are some examples: -/// - &(x) => x -/// - &*****f => f for f a function designator. -/// - &s.xx => s -/// - &s.zz[1].yy -> s, if zz is an array -/// - *(x + 1) -> x, if x is an array -/// - &"123"[2] -> 0 -/// - & __real__ x -> x -/// -/// FIXME: We don't recurse to the RHS of a comma, nor handle pointers to -/// members. -static ValueDecl *getPrimaryDecl(Expr *E) { - switch (E->getStmtClass()) { - case Stmt::DeclRefExprClass: - return cast(E)->getDecl(); - case Stmt::MemberExprClass: - // If this is an arrow operator, the address is an offset from - // the base's value, so the object the base refers to is - // irrelevant. - if (cast(E)->isArrow()) - return nullptr; - // Otherwise, the expression refers to a part of the base - return getPrimaryDecl(cast(E)->getBase()); - case Stmt::ArraySubscriptExprClass: { - // FIXME: This code shouldn't be necessary! We should catch the implicit - // promotion of register arrays earlier. - Expr* Base = cast(E)->getBase(); - if (ImplicitCastExpr* ICE = dyn_cast(Base)) { - if (ICE->getSubExpr()->getType()->isArrayType()) - return getPrimaryDecl(ICE->getSubExpr()); - } - return nullptr; - } - case Stmt::UnaryOperatorClass: { - UnaryOperator *UO = cast(E); - - switch(UO->getOpcode()) { - case UO_Real: - case UO_Imag: - case UO_Extension: - return getPrimaryDecl(UO->getSubExpr()); - default: - return nullptr; - } - } - case Stmt::ParenExprClass: - return getPrimaryDecl(cast(E)->getSubExpr()); - case Stmt::ImplicitCastExprClass: - // If the result of an implicit cast is an l-value, we care about - // the sub-expression; otherwise, the result here doesn't matter. - return getPrimaryDecl(cast(E)->getSubExpr()); - case Stmt::CXXUuidofExprClass: - return cast(E)->getGuidDecl(); - default: - return nullptr; +static QualType checkSizelessVectorShift(Sema &S, ExprResult &LHS, + ExprResult &RHS, SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = S.UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); } -} - -namespace { -enum { - AO_Bit_Field = 0, - AO_Vector_Element = 1, - AO_Property_Expansion = 2, - AO_Register_Variable = 3, - AO_Matrix_Element = 4, - AO_No_Error = 5 -}; -} -/// Diagnose invalid operand for address of operations. -/// -/// \param Type The type of operand which cannot have its address taken. -static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, - Expr *E, unsigned Type) { - S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange(); -} - -bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, - const Expr *Op, - const CXXMethodDecl *MD) { - const auto *DRE = cast(Op->IgnoreParens()); - if (Op != DRE) - return Diag(OpLoc, diag::err_parens_pointer_member_function) - << Op->getSourceRange(); + RHS = S.UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - // Taking the address of a dtor is illegal per C++ [class.dtor]p2. - if (isa(MD)) - return Diag(OpLoc, diag::err_typecheck_addrof_dtor) - << DRE->getSourceRange(); + QualType LHSType = LHS.get()->getType(); + const BuiltinType *LHSBuiltinTy = LHSType->castAs(); + QualType LHSEleType = LHSType->isSveVLSBuiltinType() + ? LHSBuiltinTy->getSveEltType(S.getASTContext()) + : LHSType; - if (DRE->getQualifier()) - return false; + // Note that RHS might not be a vector + QualType RHSType = RHS.get()->getType(); + const BuiltinType *RHSBuiltinTy = RHSType->castAs(); + QualType RHSEleType = RHSType->isSveVLSBuiltinType() + ? RHSBuiltinTy->getSveEltType(S.getASTContext()) + : RHSType; - if (MD->getParent()->getName().empty()) - return Diag(OpLoc, diag::err_unqualified_pointer_member_function) - << DRE->getSourceRange(); + if ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || + (RHSBuiltinTy && RHSBuiltinTy->isSVEBool())) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHSType << RHSType << LHS.get()->getSourceRange(); + return QualType(); + } - SmallString<32> Str; - StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str); - return Diag(OpLoc, diag::err_unqualified_pointer_member_function) - << DRE->getSourceRange() - << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual); -} + if (!LHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << LHS.get()->getType() << LHS.get()->getSourceRange(); + return QualType(); + } -QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { - if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){ - if (PTy->getKind() == BuiltinType::Overload) { - Expr *E = OrigOp.get()->IgnoreParens(); - if (!isa(E)) { - assert(cast(E)->getOpcode() == UO_AddrOf); - Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof_addrof_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } + if (!RHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << RHS.get()->getType() << RHS.get()->getSourceRange(); + return QualType(); + } - OverloadExpr *Ovl = cast(E); - if (isa(Ovl)) - if (!ResolveSingleFunctionTemplateSpecialization(Ovl)) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } + if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && + (S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != + S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC)) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - return Context.OverloadTy; + if (!LHSType->isSveVLSBuiltinType()) { + assert(RHSType->isSveVLSBuiltinType()); + if (IsCompAssign) + return RHSType; + if (LHSEleType != RHSEleType) { + LHS = S.ImpCastExprToType(LHS.get(), RHSEleType, clang::CK_IntegralCast); + LHSEleType = RHSEleType; } - - if (PTy->getKind() == BuiltinType::UnknownAny) - return Context.UnknownAnyTy; - - if (PTy->getKind() == BuiltinType::BoundMember) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); + const llvm::ElementCount VecSize = + S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC; + QualType VecTy = + S.Context.getScalableVectorType(LHSEleType, VecSize.getKnownMinValue()); + LHS = S.ImpCastExprToType(LHS.get(), VecTy, clang::CK_VectorSplat); + LHSType = VecTy; + } else if (RHSBuiltinTy && RHSBuiltinTy->isSveVLSBuiltinType()) { + if (S.Context.getTypeSize(RHSBuiltinTy) != + S.Context.getTypeSize(LHSBuiltinTy)) { + S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); return QualType(); } - - OrigOp = CheckPlaceholderExpr(OrigOp.get()); - if (OrigOp.isInvalid()) return QualType(); + } else { + const llvm::ElementCount VecSize = + S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC; + if (LHSEleType != RHSEleType) { + RHS = S.ImpCastExprToType(RHS.get(), LHSEleType, clang::CK_IntegralCast); + RHSEleType = LHSEleType; + } + QualType VecTy = + S.Context.getScalableVectorType(RHSEleType, VecSize.getKnownMinValue()); + RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); } - if (OrigOp.get()->isTypeDependent()) - return Context.DependentTy; - - assert(!OrigOp.get()->hasPlaceholderType()); + return LHSType; +} - // Make sure to ignore parentheses in subsequent checks - Expr *op = OrigOp.get()->IgnoreParens(); +// C99 6.5.7 +QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + bool IsCompAssign) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - // In OpenCL captures for blocks called as lambda functions - // are located in the private address space. Blocks used in - // enqueue_kernel can be located in a different address space - // depending on a vendor implementation. Thus preventing - // taking an address of the capture to avoid invalid AS casts. - if (LangOpts.OpenCL) { - auto* VarRef = dyn_cast(op); - if (VarRef && VarRef->refersToEnclosingVariableOrCapture()) { - Diag(op->getExprLoc(), diag::err_opencl_taking_address_capture); - return QualType(); + // Vector shifts promote their scalar inputs to vector type. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + if (LangOpts.ZVector) { + // The shift operators for the z vector extensions work basically + // like general shifts, except that neither the LHS nor the RHS is + // allowed to be a "vector bool". + if (auto LHSVecType = LHS.get()->getType()->getAs()) + if (LHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return InvalidOperands(Loc, LHS, RHS); + if (auto RHSVecType = RHS.get()->getType()->getAs()) + if (RHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return InvalidOperands(Loc, LHS, RHS); } + return checkVectorShift(*this, LHS, RHS, Loc, IsCompAssign); } - if (getLangOpts().C99) { - // Implement C99-only parts of addressof rules. - if (UnaryOperator* uOp = dyn_cast(op)) { - if (uOp->getOpcode() == UO_Deref) - // Per C99 6.5.3.2, the address of a deref always returns a valid result - // (assuming the deref expression is valid). - return uOp->getSubExpr()->getType(); - } - // Technically, there should be a check for array subscript - // expressions here, but the result of one is always an lvalue anyway. - } - ValueDecl *dcl = getPrimaryDecl(op); + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) + return checkSizelessVectorShift(*this, LHS, RHS, Loc, IsCompAssign); - if (auto *FD = dyn_cast_or_null(dcl)) - if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, - op->getBeginLoc())) - return QualType(); + // Shifts don't perform usual arithmetic conversions, they just do integer + // promotions on each operand. C99 6.5.7p3 - Expr::LValueClassification lval = op->ClassifyLValue(Context); - unsigned AddressOfError = AO_No_Error; + // For the LHS, do usual unary conversions, but then reset them away + // if this is a compound assignment. + ExprResult OldLHS = LHS; + LHS = UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + QualType LHSType = LHS.get()->getType(); + if (IsCompAssign) LHS = OldLHS; - if (lval == Expr::LV_ClassTemporary || lval == Expr::LV_ArrayTemporary) { - bool sfinae = (bool)isSFINAEContext(); - Diag(OpLoc, isSFINAEContext() ? diag::err_typecheck_addrof_temporary - : diag::ext_typecheck_addrof_temporary) - << op->getType() << op->getSourceRange(); - if (sfinae) - return QualType(); - // Materialize the temporary as an lvalue so that we can take its address. - OrigOp = op = - CreateMaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); - } else if (isa(op)) { - return Context.getPointerType(op->getType()); - } else if (lval == Expr::LV_MemberFunction) { - // If it's an instance method, make a member pointer. - // The expression must have exactly the form &A::foo. + // The RHS is simpler. + RHS = UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + QualType RHSType = RHS.get()->getType(); - // If the underlying expression isn't a decl ref, give up. - if (!isa(op)) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } - DeclRefExpr *DRE = cast(op); - CXXMethodDecl *MD = cast(DRE->getDecl()); + // C99 6.5.7p2: Each of the operands shall have integer type. + // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point. + if ((!LHSType->isFixedPointOrIntegerType() && + !LHSType->hasIntegerRepresentation()) || + !RHSType->hasIntegerRepresentation()) + return InvalidOperands(Loc, LHS, RHS); - CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); + // C++0x: Don't allow scoped enums. FIXME: Use something better than + // hasIntegerRepresentation() above instead of this. + if (isScopedEnumerationType(LHSType) || + isScopedEnumerationType(RHSType)) { + return InvalidOperands(Loc, LHS, RHS); + } + DiagnoseBadShiftValues(*this, LHS, RHS, Loc, Opc, LHSType); - QualType MPTy = Context.getMemberPointerType( - op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); + // "The type of the result is that of the promoted left operand." + return LHSType; +} - if (getLangOpts().PointerAuthCalls && MD->isVirtual() && - !isUnevaluatedContext() && !MPTy->isDependentType()) { - // When pointer authentication is enabled, argument and return types of - // vitual member functions must be complete. This is because vitrual - // member function pointers are implemented using virtual dispatch - // thunks and the thunks cannot be emitted if the argument or return - // types are incomplete. - auto ReturnOrParamTypeIsIncomplete = [&](QualType T, - SourceLocation DeclRefLoc, - SourceLocation RetArgTypeLoc) { - if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { - Diag(DeclRefLoc, - diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); - Diag(RetArgTypeLoc, - diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) - << T; - return true; - } - return false; - }; - QualType RetTy = MD->getReturnType(); - bool IsIncomplete = - !RetTy->isVoidType() && - ReturnOrParamTypeIsIncomplete( - RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); - for (auto *PVD : MD->parameters()) - IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, - PVD->getBeginLoc()); - if (IsIncomplete) - return QualType(); - } +/// Diagnose bad pointer comparisons. +static void diagnoseDistinctPointerComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS, + bool IsError) { + S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_distinct_pointers + : diag::ext_typecheck_comparison_of_distinct_pointers) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); +} - // Under the MS ABI, lock down the inheritance model now. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) - (void)isCompleteType(OpLoc, MPTy); - return MPTy; - } else if (lval != Expr::LV_Valid && lval != Expr::LV_IncompleteVoidType) { - // C99 6.5.3.2p1 - // The operand must be either an l-value or a function designator - if (!op->getType()->isFunctionType()) { - // Use a special diagnostic for loads from property references. - if (isa(op)) { - AddressOfError = AO_Property_Expansion; - } else { - Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) - << op->getType() << op->getSourceRange(); - return QualType(); - } - } else if (const auto *DRE = dyn_cast(op)) { - if (const auto *MD = dyn_cast_or_null(DRE->getDecl())) - CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); - } +/// Returns false if the pointers are converted to a composite type, +/// true otherwise. +static bool convertPointersToCompositeType(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS) { + // C++ [expr.rel]p2: + // [...] Pointer conversions (4.10) and qualification + // conversions (4.4) are performed on pointer operands (or on + // a pointer operand and a null pointer constant) to bring + // them to their composite pointer type. [...] + // + // C++ [expr.eq]p1 uses the same notion for (in)equality + // comparisons of pointers. - } else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1 - // The operand cannot be a bit-field - AddressOfError = AO_Bit_Field; - } else if (op->getObjectKind() == OK_VectorComponent) { - // The operand cannot be an element of a vector - AddressOfError = AO_Vector_Element; - } else if (op->getObjectKind() == OK_MatrixComponent) { - // The operand cannot be an element of a matrix. - AddressOfError = AO_Matrix_Element; - } else if (dcl) { // C99 6.5.3.2p1 - // We have an lvalue with a decl. Make sure the decl is not declared - // with the register storage-class specifier. - if (const VarDecl *vd = dyn_cast(dcl)) { - // in C++ it is not error to take address of a register - // variable (c++03 7.1.1P3) - if (vd->getStorageClass() == SC_Register && - !getLangOpts().CPlusPlus) { - AddressOfError = AO_Register_Variable; - } - } else if (isa(dcl)) { - AddressOfError = AO_Property_Expansion; - } else if (isa(dcl)) { - return Context.OverloadTy; - } else if (isa(dcl) || isa(dcl)) { - // Okay: we can take the address of a field. - // Could be a pointer to member, though, if there is an explicit - // scope qualifier for the class. + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + assert(LHSType->isPointerType() || RHSType->isPointerType() || + LHSType->isMemberPointerType() || RHSType->isMemberPointerType()); - // [C++26] [expr.prim.id.general] - // If an id-expression E denotes a non-static non-type member - // of some class C [...] and if E is a qualified-id, E is - // not the un-parenthesized operand of the unary & operator [...] - // the id-expression is transformed into a class member access expression. - if (isa(op) && cast(op)->getQualifier() && - !isa(OrigOp.get())) { - DeclContext *Ctx = dcl->getDeclContext(); - if (Ctx && Ctx->isRecord()) { - if (dcl->getType()->isReferenceType()) { - Diag(OpLoc, - diag::err_cannot_form_pointer_to_member_of_reference_type) - << dcl->getDeclName() << dcl->getType(); - return QualType(); - } + QualType T = S.FindCompositePointerType(Loc, LHS, RHS); + if (T.isNull()) { + if ((LHSType->isAnyPointerType() || LHSType->isMemberPointerType()) && + (RHSType->isAnyPointerType() || RHSType->isMemberPointerType())) + diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); + else + S.InvalidOperands(Loc, LHS, RHS); + return true; + } - while (cast(Ctx)->isAnonymousStructOrUnion()) - Ctx = Ctx->getParent(); + return false; +} - QualType MPTy = Context.getMemberPointerType( - op->getType(), - Context.getTypeDeclType(cast(Ctx)).getTypePtr()); - // Under the MS ABI, lock down the inheritance model now. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) - (void)isCompleteType(OpLoc, MPTy); - return MPTy; - } - } - } else if (!isa(dcl)) - llvm_unreachable("Unknown/unexpected decl type"); - } +static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, + ExprResult &RHS, + bool IsError) { + S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_fptr_to_void + : diag::ext_typecheck_comparison_of_fptr_to_void) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); +} - if (AddressOfError != AO_No_Error) { - diagnoseAddressOfInvalidType(*this, OpLoc, op, AddressOfError); - return QualType(); +static bool isObjCObjectLiteral(ExprResult &E) { + switch (E.get()->IgnoreParenImpCasts()->getStmtClass()) { + case Stmt::ObjCArrayLiteralClass: + case Stmt::ObjCDictionaryLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ObjCBoxedExprClass: + return true; + default: + // Note that ObjCBoolLiteral is NOT an object literal! + return false; } +} - if (lval == Expr::LV_IncompleteVoidType) { - // Taking the address of a void variable is technically illegal, but we - // allow it in cases which are otherwise valid. - // Example: "extern void x; void* y = &x;". - Diag(OpLoc, diag::ext_typecheck_addrof_void) << op->getSourceRange(); - } +static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) { + const ObjCObjectPointerType *Type = + LHS->getType()->getAs(); - // If the operand has type "type", the result has type "pointer to type". - if (op->getType()->isObjCObjectType()) - return Context.getObjCObjectPointerType(op->getType()); + // If this is not actually an Objective-C object, bail out. + if (!Type) + return false; - // Cannot take the address of WebAssembly references or tables. - if (Context.getTargetInfo().getTriple().isWasm()) { - QualType OpTy = op->getType(); - if (OpTy.isWebAssemblyReferenceType()) { - Diag(OpLoc, diag::err_wasm_ca_reference) - << 1 << OrigOp.get()->getSourceRange(); - return QualType(); - } - if (OpTy->isWebAssemblyTableType()) { - Diag(OpLoc, diag::err_wasm_table_pr) - << 1 << OrigOp.get()->getSourceRange(); - return QualType(); + // Get the LHS object's interface type. + QualType InterfaceType = Type->getPointeeType(); + + // If the RHS isn't an Objective-C object, bail out. + if (!RHS->getType()->isObjCObjectPointerType()) + return false; + + // Try to find the -isEqual: method. + Selector IsEqualSel = S.ObjC().NSAPIObj->getIsEqualSelector(); + ObjCMethodDecl *Method = + S.ObjC().LookupMethodInObjectType(IsEqualSel, InterfaceType, + /*IsInstance=*/true); + if (!Method) { + if (Type->isObjCIdType()) { + // For 'id', just check the global pool. + Method = + S.ObjC().LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(), + /*receiverId=*/true); + } else { + // Check protocols. + Method = S.ObjC().LookupMethodInQualifiedType(IsEqualSel, Type, + /*IsInstance=*/true); } } - CheckAddressOfPackedMember(op); + if (!Method) + return false; - return Context.getPointerType(op->getType()); -} + QualType T = Method->parameters()[0]->getType(); + if (!T->isObjCObjectPointerType()) + return false; -static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) { - const DeclRefExpr *DRE = dyn_cast(Exp); - if (!DRE) - return; - const Decl *D = DRE->getDecl(); - if (!D) - return; - const ParmVarDecl *Param = dyn_cast(D); - if (!Param) - return; - if (const FunctionDecl* FD = dyn_cast(Param->getDeclContext())) - if (!FD->hasAttr() && !Param->hasAttr()) - return; - if (FunctionScopeInfo *FD = S.getCurFunction()) - FD->ModifiedNonNullParams.insert(Param); -} + QualType R = Method->getReturnType(); + if (!R->isScalarType()) + return false; -/// CheckIndirectionOperand - Type check unary indirection (prefix '*'). -static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, - SourceLocation OpLoc, - bool IsAfterAmp = false) { - ExprResult ConvResult = S.UsualUnaryConversions(Op); - if (ConvResult.isInvalid()) - return QualType(); - Op = ConvResult.get(); - QualType OpTy = Op->getType(); - QualType Result; + return true; +} - if (isa(Op)) { - QualType OpOrigType = Op->IgnoreParenCasts()->getType(); - S.CheckCompatibleReinterpretCast(OpOrigType, OpTy, /*IsDereference*/true, - Op->getSourceRange()); +static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS, + BinaryOperator::Opcode Opc){ + Expr *Literal; + Expr *Other; + if (isObjCObjectLiteral(LHS)) { + Literal = LHS.get(); + Other = RHS.get(); + } else { + Literal = RHS.get(); + Other = LHS.get(); } - if (const PointerType *PT = OpTy->getAs()) - { - Result = PT->getPointeeType(); - } - else if (const ObjCObjectPointerType *OPT = - OpTy->getAs()) - Result = OPT->getPointeeType(); - else { - ExprResult PR = S.CheckPlaceholderExpr(Op); - if (PR.isInvalid()) return QualType(); - if (PR.get() != Op) - return CheckIndirectionOperand(S, PR.get(), VK, OpLoc); - } + // Don't warn on comparisons against nil. + Other = Other->IgnoreParenCasts(); + if (Other->isNullPointerConstant(S.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) + return; - if (Result.isNull()) { - S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer) - << OpTy << Op->getSourceRange(); - return QualType(); + // This should be kept in sync with warn_objc_literal_comparison. + // LK_String should always be after the other literals, since it has its own + // warning flag. + SemaObjC::ObjCLiteralKind LiteralKind = S.ObjC().CheckLiteralKind(Literal); + assert(LiteralKind != SemaObjC::LK_Block); + if (LiteralKind == SemaObjC::LK_None) { + llvm_unreachable("Unknown Objective-C object literal kind"); } - if (Result->isVoidType()) { - // C++ [expr.unary.op]p1: - // [...] the expression to which [the unary * operator] is applied shall - // be a pointer to an object type, or a pointer to a function type - LangOptions LO = S.getLangOpts(); - if (LO.CPlusPlus) - S.Diag(OpLoc, diag::err_typecheck_indirection_through_void_pointer_cpp) - << OpTy << Op->getSourceRange(); - else if (!(LO.C99 && IsAfterAmp) && !S.isUnevaluatedContext()) - S.Diag(OpLoc, diag::ext_typecheck_indirection_through_void_pointer) - << OpTy << Op->getSourceRange(); + if (LiteralKind == SemaObjC::LK_String) + S.Diag(Loc, diag::warn_objc_string_literal_comparison) + << Literal->getSourceRange(); + else + S.Diag(Loc, diag::warn_objc_literal_comparison) + << LiteralKind << Literal->getSourceRange(); + + if (BinaryOperator::isEqualityOp(Opc) && + hasIsEqualMethod(S, LHS.get(), RHS.get())) { + SourceLocation Start = LHS.get()->getBeginLoc(); + SourceLocation End = S.getLocForEndOfToken(RHS.get()->getEndLoc()); + CharSourceRange OpRange = + CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + + S.Diag(Loc, diag::note_objc_literal_comparison_isequal) + << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![") + << FixItHint::CreateReplacement(OpRange, " isEqual:") + << FixItHint::CreateInsertion(End, "]"); } +} - // Dereferences are usually l-values... - VK = VK_LValue; +/// Warns on !x < y, !x & y where !(x < y), !(x & y) was probably intended. +static void diagnoseLogicalNotOnLHSofCheck(Sema &S, ExprResult &LHS, + ExprResult &RHS, SourceLocation Loc, + BinaryOperatorKind Opc) { + // Check that left hand side is !something. + UnaryOperator *UO = dyn_cast(LHS.get()->IgnoreImpCasts()); + if (!UO || UO->getOpcode() != UO_LNot) return; - // ...except that certain expressions are never l-values in C. - if (!S.getLangOpts().CPlusPlus && Result.isCForbiddenLValueType()) - VK = VK_PRValue; + // Only check if the right hand side is non-bool arithmetic type. + if (RHS.get()->isKnownToHaveBooleanValue()) return; - return Result; -} + // Make sure that the something in !something is not bool. + Expr *SubExpr = UO->getSubExpr()->IgnoreImpCasts(); + if (SubExpr->isKnownToHaveBooleanValue()) return; -BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) { - BinaryOperatorKind Opc; - switch (Kind) { - default: llvm_unreachable("Unknown binop!"); - case tok::periodstar: Opc = BO_PtrMemD; break; - case tok::arrowstar: Opc = BO_PtrMemI; break; - case tok::star: Opc = BO_Mul; break; - case tok::slash: Opc = BO_Div; break; - case tok::percent: Opc = BO_Rem; break; - case tok::plus: Opc = BO_Add; break; - case tok::minus: Opc = BO_Sub; break; - case tok::lessless: Opc = BO_Shl; break; - case tok::greatergreater: Opc = BO_Shr; break; - case tok::lessequal: Opc = BO_LE; break; - case tok::less: Opc = BO_LT; break; - case tok::greaterequal: Opc = BO_GE; break; - case tok::greater: Opc = BO_GT; break; - case tok::exclaimequal: Opc = BO_NE; break; - case tok::equalequal: Opc = BO_EQ; break; - case tok::spaceship: Opc = BO_Cmp; break; - case tok::amp: Opc = BO_And; break; - case tok::caret: Opc = BO_Xor; break; - case tok::pipe: Opc = BO_Or; break; - case tok::ampamp: Opc = BO_LAnd; break; - case tok::pipepipe: Opc = BO_LOr; break; - case tok::equal: Opc = BO_Assign; break; - case tok::starequal: Opc = BO_MulAssign; break; - case tok::slashequal: Opc = BO_DivAssign; break; - case tok::percentequal: Opc = BO_RemAssign; break; - case tok::plusequal: Opc = BO_AddAssign; break; - case tok::minusequal: Opc = BO_SubAssign; break; - case tok::lesslessequal: Opc = BO_ShlAssign; break; - case tok::greatergreaterequal: Opc = BO_ShrAssign; break; - case tok::ampequal: Opc = BO_AndAssign; break; - case tok::caretequal: Opc = BO_XorAssign; break; - case tok::pipeequal: Opc = BO_OrAssign; break; - case tok::comma: Opc = BO_Comma; break; - } - return Opc; + // Emit warning. + bool IsBitwiseOp = Opc == BO_And || Opc == BO_Or || Opc == BO_Xor; + S.Diag(UO->getOperatorLoc(), diag::warn_logical_not_on_lhs_of_check) + << Loc << IsBitwiseOp; + + // First note suggest !(x < y) + SourceLocation FirstOpen = SubExpr->getBeginLoc(); + SourceLocation FirstClose = RHS.get()->getEndLoc(); + FirstClose = S.getLocForEndOfToken(FirstClose); + if (FirstClose.isInvalid()) + FirstOpen = SourceLocation(); + S.Diag(UO->getOperatorLoc(), diag::note_logical_not_fix) + << IsBitwiseOp + << FixItHint::CreateInsertion(FirstOpen, "(") + << FixItHint::CreateInsertion(FirstClose, ")"); + + // Second note suggests (!x) < y + SourceLocation SecondOpen = LHS.get()->getBeginLoc(); + SourceLocation SecondClose = LHS.get()->getEndLoc(); + SecondClose = S.getLocForEndOfToken(SecondClose); + if (SecondClose.isInvalid()) + SecondOpen = SourceLocation(); + S.Diag(UO->getOperatorLoc(), diag::note_logical_not_silence_with_parens) + << FixItHint::CreateInsertion(SecondOpen, "(") + << FixItHint::CreateInsertion(SecondClose, ")"); } -static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( - tok::TokenKind Kind) { - UnaryOperatorKind Opc; - switch (Kind) { - default: llvm_unreachable("Unknown unary op!"); - case tok::plusplus: Opc = UO_PreInc; break; - case tok::minusminus: Opc = UO_PreDec; break; - case tok::amp: Opc = UO_AddrOf; break; - case tok::star: Opc = UO_Deref; break; - case tok::plus: Opc = UO_Plus; break; - case tok::minus: Opc = UO_Minus; break; - case tok::tilde: Opc = UO_Not; break; - case tok::exclaim: Opc = UO_LNot; break; - case tok::kw___real: Opc = UO_Real; break; - case tok::kw___imag: Opc = UO_Imag; break; - case tok::kw___extension__: Opc = UO_Extension; break; +// Returns true if E refers to a non-weak array. +static bool checkForArray(const Expr *E) { + const ValueDecl *D = nullptr; + if (const DeclRefExpr *DR = dyn_cast(E)) { + D = DR->getDecl(); + } else if (const MemberExpr *Mem = dyn_cast(E)) { + if (Mem->isImplicitAccess()) + D = Mem->getMemberDecl(); } - return Opc; + if (!D) + return false; + return D->getType()->isArrayType() && !D->isWeak(); } -const FieldDecl * -Sema::getSelfAssignmentClassMemberCandidate(const ValueDecl *SelfAssigned) { - // Explore the case for adding 'this->' to the LHS of a self assignment, very - // common for setters. - // struct A { - // int X; - // -void setX(int X) { X = X; } - // +void setX(int X) { this->X = X; } - // }; +/// Diagnose some forms of syntactically-obvious tautological comparison. +static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS, + BinaryOperatorKind Opc) { + Expr *LHSStripped = LHS->IgnoreParenImpCasts(); + Expr *RHSStripped = RHS->IgnoreParenImpCasts(); - // Only consider parameters for self assignment fixes. - if (!isa(SelfAssigned)) - return nullptr; - const auto *Method = - dyn_cast_or_null(getCurFunctionDecl(true)); - if (!Method) - return nullptr; + QualType LHSType = LHS->getType(); + QualType RHSType = RHS->getType(); + if (LHSType->hasFloatingRepresentation() || + (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || + S.inTemplateInstantiation()) + return; - const CXXRecordDecl *Parent = Method->getParent(); - // In theory this is fixable if the lambda explicitly captures this, but - // that's added complexity that's rarely going to be used. - if (Parent->isLambda()) - return nullptr; - - // FIXME: Use an actual Lookup operation instead of just traversing fields - // in order to get base class fields. - auto Field = - llvm::find_if(Parent->fields(), - [Name(SelfAssigned->getDeclName())](const FieldDecl *F) { - return F->getDeclName() == Name; - }); - return (Field != Parent->field_end()) ? *Field : nullptr; -} - -/// DiagnoseSelfAssignment - Emits a warning if a value is assigned to itself. -/// This warning suppressed in the event of macro expansions. -static void DiagnoseSelfAssignment(Sema &S, Expr *LHSExpr, Expr *RHSExpr, - SourceLocation OpLoc, bool IsBuiltin) { - if (S.inTemplateInstantiation()) - return; - if (S.isUnevaluatedContext()) - return; - if (OpLoc.isInvalid() || OpLoc.isMacroID()) - return; - LHSExpr = LHSExpr->IgnoreParenImpCasts(); - RHSExpr = RHSExpr->IgnoreParenImpCasts(); - const DeclRefExpr *LHSDeclRef = dyn_cast(LHSExpr); - const DeclRefExpr *RHSDeclRef = dyn_cast(RHSExpr); - if (!LHSDeclRef || !RHSDeclRef || - LHSDeclRef->getLocation().isMacroID() || - RHSDeclRef->getLocation().isMacroID()) - return; - const ValueDecl *LHSDecl = - cast(LHSDeclRef->getDecl()->getCanonicalDecl()); - const ValueDecl *RHSDecl = - cast(RHSDeclRef->getDecl()->getCanonicalDecl()); - if (LHSDecl != RHSDecl) - return; - if (LHSDecl->getType().isVolatileQualified()) + // WebAssembly Tables cannot be compared, therefore shouldn't emit + // Tautological diagnostics. + if (LHSType->isWebAssemblyTableType() || RHSType->isWebAssemblyTableType()) return; - if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) - if (RefTy->getPointeeType().isVolatileQualified()) - return; - - auto Diag = S.Diag(OpLoc, IsBuiltin ? diag::warn_self_assignment_builtin - : diag::warn_self_assignment_overloaded) - << LHSDeclRef->getType() << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); - if (const FieldDecl *SelfAssignField = - S.getSelfAssignmentClassMemberCandidate(RHSDecl)) - Diag << 1 << SelfAssignField - << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->"); - else - Diag << 0; -} -/// Check if a bitwise-& is performed on an Objective-C pointer. This -/// is usually indicative of introspection within the Objective-C pointer. -static void checkObjCPointerIntrospection(Sema &S, ExprResult &L, ExprResult &R, - SourceLocation OpLoc) { - if (!S.getLangOpts().ObjC) + // Comparisons between two array types are ill-formed for operator<=>, so + // we shouldn't emit any additional warnings about it. + if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) return; - const Expr *ObjCPointerExpr = nullptr, *OtherExpr = nullptr; - const Expr *LHS = L.get(); - const Expr *RHS = R.get(); + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + // + // NOTE: Don't warn about comparison expressions resulting from macro + // expansion. Also don't warn about comparisons which are only self + // comparisons within a template instantiation. The warnings should catch + // obvious cases in the definition of the template anyways. The idea is to + // warn when the typed comparison operator will always evaluate to the same + // result. - if (LHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { - ObjCPointerExpr = LHS; - OtherExpr = RHS; - } - else if (RHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { - ObjCPointerExpr = RHS; - OtherExpr = LHS; + // Used for indexing into %select in warn_comparison_always + enum { + AlwaysConstant, + AlwaysTrue, + AlwaysFalse, + AlwaysEqual, // std::strong_ordering::equal from operator<=> + }; + + // C++2a [depr.array.comp]: + // Equality and relational comparisons ([expr.eq], [expr.rel]) between two + // operands of array type are deprecated. + if (S.getLangOpts().CPlusPlus20 && LHSStripped->getType()->isArrayType() && + RHSStripped->getType()->isArrayType()) { + S.Diag(Loc, diag::warn_depr_array_comparison) + << LHS->getSourceRange() << RHS->getSourceRange() + << LHSStripped->getType() << RHSStripped->getType(); + // Carry on to produce the tautological comparison warning, if this + // expression is potentially-evaluated, we can resolve the array to a + // non-weak declaration, and so on. } - // This warning is deliberately made very specific to reduce false - // positives with logic that uses '&' for hashing. This logic mainly - // looks for code trying to introspect into tagged pointers, which - // code should generally never do. - if (ObjCPointerExpr && isa(OtherExpr->IgnoreParenCasts())) { - unsigned Diag = diag::warn_objc_pointer_masking; - // Determine if we are introspecting the result of performSelectorXXX. - const Expr *Ex = ObjCPointerExpr->IgnoreParenCasts(); - // Special case messages to -performSelector and friends, which - // can return non-pointer values boxed in a pointer value. - // Some clients may wish to silence warnings in this subcase. - if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { - Selector S = ME->getSelector(); - StringRef SelArg0 = S.getNameForSlot(0); - if (SelArg0.starts_with("performSelector")) - Diag = diag::warn_objc_pointer_masking_performSelector; + if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) { + if (Expr::isSameComparisonOperand(LHS, RHS)) { + unsigned Result; + switch (Opc) { + case BO_EQ: + case BO_LE: + case BO_GE: + Result = AlwaysTrue; + break; + case BO_NE: + case BO_LT: + case BO_GT: + Result = AlwaysFalse; + break; + case BO_Cmp: + Result = AlwaysEqual; + break; + default: + Result = AlwaysConstant; + break; + } + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_comparison_always) + << 0 /*self-comparison*/ + << Result); + } else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) { + // What is it always going to evaluate to? + unsigned Result; + switch (Opc) { + case BO_EQ: // e.g. array1 == array2 + Result = AlwaysFalse; + break; + case BO_NE: // e.g. array1 != array2 + Result = AlwaysTrue; + break; + default: // e.g. array1 <= array2 + // The best we can say is 'a constant' + Result = AlwaysConstant; + break; + } + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_comparison_always) + << 1 /*array comparison*/ + << Result); } - - S.Diag(OpLoc, Diag) - << ObjCPointerExpr->getSourceRange(); } -} - -static NamedDecl *getDeclFromExpr(Expr *E) { - if (!E) - return nullptr; - if (auto *DRE = dyn_cast(E)) - return DRE->getDecl(); - if (auto *ME = dyn_cast(E)) - return ME->getMemberDecl(); - if (auto *IRE = dyn_cast(E)) - return IRE->getDecl(); - return nullptr; -} - -// This helper function promotes a binary operator's operands (which are of a -// half vector type) to a vector of floats and then truncates the result to -// a vector of either half or short. -static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, - BinaryOperatorKind Opc, QualType ResultTy, - ExprValueKind VK, ExprObjectKind OK, - bool IsCompAssign, SourceLocation OpLoc, - FPOptionsOverride FPFeatures) { - auto &Context = S.getASTContext(); - assert((isVector(ResultTy, Context.HalfTy) || - isVector(ResultTy, Context.ShortTy)) && - "Result must be a vector of half or short"); - assert(isVector(LHS.get()->getType(), Context.HalfTy) && - isVector(RHS.get()->getType(), Context.HalfTy) && - "both operands expected to be a half vector"); - - RHS = convertVector(RHS.get(), Context.FloatTy, S); - QualType BinOpResTy = RHS.get()->getType(); - - // If Opc is a comparison, ResultType is a vector of shorts. In that case, - // change BinOpResTy to a vector of ints. - if (isVector(ResultTy, Context.ShortTy)) - BinOpResTy = S.GetSignedVectorType(BinOpResTy); - if (IsCompAssign) - return CompoundAssignOperator::Create(Context, LHS.get(), RHS.get(), Opc, - ResultTy, VK, OK, OpLoc, FPFeatures, - BinOpResTy, BinOpResTy); + if (isa(LHSStripped)) + LHSStripped = LHSStripped->IgnoreParenCasts(); + if (isa(RHSStripped)) + RHSStripped = RHSStripped->IgnoreParenCasts(); - LHS = convertVector(LHS.get(), Context.FloatTy, S); - auto *BO = BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, - BinOpResTy, VK, OK, OpLoc, FPFeatures); - return convertVector(BO, ResultTy->castAs()->getElementType(), S); -} + // Warn about comparisons against a string constant (unless the other + // operand is null); the user probably wants string comparison function. + Expr *LiteralString = nullptr; + Expr *LiteralStringStripped = nullptr; + if ((isa(LHSStripped) || isa(LHSStripped)) && + !RHSStripped->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) { + LiteralString = LHS; + LiteralStringStripped = LHSStripped; + } else if ((isa(RHSStripped) || + isa(RHSStripped)) && + !LHSStripped->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) { + LiteralString = RHS; + LiteralStringStripped = RHSStripped; + } -static std::pair -CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, - Expr *RHSExpr) { - ExprResult LHS = LHSExpr, RHS = RHSExpr; - if (!S.Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes on either side of a binop because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - LHS = S.CorrectDelayedTyposInExpr(LHS); - RHS = S.CorrectDelayedTyposInExpr( - RHS, /*InitDecl=*/nullptr, /*RecoverUncorrectedTypos=*/false, - [Opc, LHS](Expr *E) { - if (Opc != BO_Assign) - return ExprResult(E); - // Avoid correcting the RHS to the same Expr as the LHS. - Decl *D = getDeclFromExpr(E); - return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; - }); + if (LiteralString) { + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_stringcompare) + << isa(LiteralStringStripped) + << LiteralString->getSourceRange()); } - return std::make_pair(LHS, RHS); } -/// Returns true if conversion between vectors of halfs and vectors of floats -/// is needed. -static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, - Expr *E0, Expr *E1 = nullptr) { - if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType || - Ctx.getTargetInfo().useFP16ConversionIntrinsics()) - return false; +static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { + switch (CK) { + default: { +#ifndef NDEBUG + llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK) + << "\n"; +#endif + llvm_unreachable("unhandled cast kind"); + } + case CK_UserDefinedConversion: + return ICK_Identity; + case CK_LValueToRValue: + return ICK_Lvalue_To_Rvalue; + case CK_ArrayToPointerDecay: + return ICK_Array_To_Pointer; + case CK_FunctionToPointerDecay: + return ICK_Function_To_Pointer; + case CK_IntegralCast: + return ICK_Integral_Conversion; + case CK_FloatingCast: + return ICK_Floating_Conversion; + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + return ICK_Floating_Integral; + case CK_IntegralComplexCast: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralComplexToFloatingComplex: + return ICK_Complex_Conversion; + case CK_FloatingComplexToReal: + case CK_FloatingRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralRealToComplex: + return ICK_Complex_Real; + case CK_HLSLArrayRValue: + return ICK_HLSL_Array_RValue; + } +} - auto HasVectorOfHalfType = [&Ctx](Expr *E) { - QualType Ty = E->IgnoreImplicit()->getType(); +static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E, + QualType FromType, + SourceLocation Loc) { + // Check for a narrowing implicit conversion. + StandardConversionSequence SCS; + SCS.setAsIdentityConversion(); + SCS.setToType(0, FromType); + SCS.setToType(1, ToType); + if (const auto *ICE = dyn_cast(E)) + SCS.Second = castKindToImplicitConversionKind(ICE->getCastKind()); - // Don't promote half precision neon vectors like float16x4_t in arm_neon.h - // to vectors of floats. Although the element type of the vectors is __fp16, - // the vectors shouldn't be treated as storage-only types. See the - // discussion here: https://reviews.llvm.org/rG825235c140e7 - if (const VectorType *VT = Ty->getAs()) { - if (VT->getVectorKind() == VectorKind::Neon) - return false; - return VT->getElementType().getCanonicalType() == Ctx.HalfTy; - } + APValue PreNarrowingValue; + QualType PreNarrowingType; + switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, + PreNarrowingType, + /*IgnoreFloatToIntegralConversion*/ true)) { + case NK_Dependent_Narrowing: + // Implicit conversion to a narrower type, but the expression is + // value-dependent so we can't tell whether it's actually narrowing. + case NK_Not_Narrowing: return false; - }; - return HasVectorOfHalfType(E0) && (!E1 || HasVectorOfHalfType(E1)); -} + case NK_Constant_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 1 + << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; + return true; -ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { - if (getLangOpts().CPlusPlus11 && isa(RHSExpr)) { - // The syntax only allows initializer lists on the RHS of assignment, - // so we don't need to worry about accepting invalid code for - // non-assignment operators. - // C++11 5.17p9: - // The meaning of x = {v} [...] is that of x = T(v) [...]. The meaning - // of x = {} is x = T(). - InitializationKind Kind = InitializationKind::CreateDirectList( - RHSExpr->getBeginLoc(), RHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - InitializedEntity Entity = - InitializedEntity::InitializeTemporary(LHSExpr->getType()); - InitializationSequence InitSeq(*this, Entity, Kind, RHSExpr); - ExprResult Init = InitSeq.Perform(*this, Entity, Kind, RHSExpr); - if (Init.isInvalid()) - return Init; - RHSExpr = Init.get(); + case NK_Variable_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + case NK_Type_Narrowing: + S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 0 << FromType << ToType; + // TODO: It's not a constant expression, but what if the user intended it + // to be? Can we produce notes to help them figure out why it isn't? + return true; } + llvm_unreachable("unhandled case in switch"); +} - ExprResult LHS = LHSExpr, RHS = RHSExpr; - QualType ResultTy; // Result type of the binary operator. - // The following two variables are used for compound assignment operators - QualType CompLHSTy; // Type of LHS after promotions for computation - QualType CompResultTy; // Type of computation result - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - bool ConvertHalfVec = false; +static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + // Dig out the original argument type and expression before implicit casts + // were applied. These are the types/expressions we need to check the + // [expr.spaceship] requirements against. + ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts(); + ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts(); + QualType LHSStrippedType = LHSStripped.get()->getType(); + QualType RHSStrippedType = RHSStripped.get()->getType(); - std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); - if (!LHS.isUsable() || !RHS.isUsable()) - return ExprError(); + // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the + // other is not, the program is ill-formed. + if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } - if (getLangOpts().OpenCL) { - QualType LHSTy = LHSExpr->getType(); - QualType RHSTy = RHSExpr->getType(); - // OpenCLC v2.0 s6.13.11.1 allows atomic variables to be initialized by - // the ATOMIC_VAR_INIT macro. - if (LHSTy->isAtomicType() || RHSTy->isAtomicType()) { - SourceRange SR(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - if (BO_Assign == Opc) - Diag(OpLoc, diag::err_opencl_atomic_init) << 0 << SR; - else - ResultTy = InvalidOperands(OpLoc, LHS, RHS); - return ExprError(); + // FIXME: Consider combining this with checkEnumArithmeticConversions. + int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() + + RHSStrippedType->isEnumeralType(); + if (NumEnumArgs == 1) { + bool LHSIsEnum = LHSStrippedType->isEnumeralType(); + QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType; + if (OtherTy->hasFloatingRepresentation()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); } - - // OpenCL special types - image, sampler, pipe, and blocks are to be used - // only with a builtin functions and therefore should be disallowed here. - if (LHSTy->isImageType() || RHSTy->isImageType() || - LHSTy->isSamplerT() || RHSTy->isSamplerT() || - LHSTy->isPipeType() || RHSTy->isPipeType() || - LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) { - ResultTy = InvalidOperands(OpLoc, LHS, RHS); - return ExprError(); + } + if (NumEnumArgs == 2) { + // C++2a [expr.spaceship]p5: If both operands have the same enumeration + // type E, the operator yields the result of converting the operands + // to the underlying type of E and applying <=> to the converted operands. + if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) { + S.InvalidOperands(Loc, LHS, RHS); + return QualType(); } + QualType IntType = + LHSStrippedType->castAs()->getDecl()->getIntegerType(); + assert(IntType->isArithmeticType()); + + // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we + // promote the boolean type, and all other promotable integer types, to + // avoid this. + if (S.Context.isPromotableIntegerType(IntType)) + IntType = S.Context.getPromotedIntegerType(IntType); + + LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast); + RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast); + LHSType = RHSType = IntType; } - checkTypeSupport(LHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); - checkTypeSupport(RHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); + // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the + // usual arithmetic conversions are applied to the operands. + QualType Type = + S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); - 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); + std::optional CCT = + getComparisonCategoryForBuiltinCmp(Type); + if (!CCT) + return S.InvalidOperands(Loc, LHS, RHS); - // 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(); + bool HasNarrowing = checkThreeWayNarrowingConversion( + S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc()); + HasNarrowing |= checkThreeWayNarrowingConversion(S, Type, RHS.get(), RHSType, + RHS.get()->getBeginLoc()); + if (HasNarrowing) + return QualType(); - 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(); + assert(!Type.isNull() && "composite type for <=> has not been set"); - 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); + return S.CheckComparisonCategoryType( + *CCT, Loc, Sema::ComparisonCategoryUsage::OperatorInExpression); +} - if (const auto *BI = dyn_cast(LHSExpr); - BI && BI->isComparisonOp()) - Diag(OpLoc, diag::warn_consecutive_comparison); +static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) + return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); - 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); - [[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); - [[fallthrough]]; - case BO_XorAssign: - CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_Comma: - ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); - if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { - VK = RHS.get()->getValueKind(); - OK = RHS.get()->getObjectKind(); - } - break; - } - if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) - return ExprError(); + // C99 6.5.8p3 / C99 6.5.9p4 + QualType Type = + S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); + assert(Type->isArithmeticType() || Type->isEnumeralType()); - // Some of the binary operations require promoting operands of half vector to - // float vectors and truncating the result back to half vector. For now, we do - // this only when HalfArgsAndReturn is set (that is, when the target is arm or - // arm64). - assert( - (Opc == BO_Comma || isVector(RHS.get()->getType(), Context.HalfTy) == - isVector(LHS.get()->getType(), Context.HalfTy)) && - "both sides are half vectors or neither sides are"); - ConvertHalfVec = - needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get()); + if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) + return S.InvalidOperands(Loc, LHS, RHS); - // Check for array bounds violations for both sides of the BinaryOperator - CheckArrayAccess(LHS.get()); - CheckArrayAccess(RHS.get()); + // Check for comparisons of floating point operands using != and ==. + if (Type->hasFloatingRepresentation()) + S.CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); - if (const ObjCIsaExpr *OISA = dyn_cast(LHS.get()->IgnoreParenCasts())) { - NamedDecl *ObjectSetClass = LookupSingleName(TUScope, - &Context.Idents.get("object_setClass"), - SourceLocation(), LookupOrdinaryName); - if (ObjectSetClass && isa(LHS.get())) { - SourceLocation RHSLocEnd = getLocForEndOfToken(RHS.get()->getEndLoc()); - Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign) - << FixItHint::CreateInsertion(LHS.get()->getBeginLoc(), - "object_setClass(") - << FixItHint::CreateReplacement(SourceRange(OISA->getOpLoc(), OpLoc), - ",") - << FixItHint::CreateInsertion(RHSLocEnd, ")"); - } - else - Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign); - } - else if (const ObjCIvarRefExpr *OIRE = - dyn_cast(LHS.get()->IgnoreParenCasts())) - DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get()); + // The result of comparisons is 'bool' in C++, 'int' in C. + return S.Context.getLogicalOperationType(); +} - // Opc is not a compound assignment if CompResultTy is null. - if (CompResultTy.isNull()) { - if (ConvertHalfVec) - return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false, - OpLoc, CurFPFeatureOverrides()); - return BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, ResultTy, - VK, OK, OpLoc, CurFPFeatureOverrides()); +void Sema::CheckPtrComparisonWithNullChar(ExprResult &E, ExprResult &NullE) { + if (!NullE.get()->getType()->isAnyPointerType()) + return; + int NullValue = PP.isMacroDefined("NULL") ? 0 : 1; + if (!E.get()->getType()->isAnyPointerType() && + E.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull) == + Expr::NPCK_ZeroExpression) { + if (const auto *CL = dyn_cast(E.get())) { + if (CL->getValue() == 0) + Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) + << NullValue + << FixItHint::CreateReplacement(E.get()->getExprLoc(), + NullValue ? "NULL" : "(void *)0"); + } else if (const auto *CE = dyn_cast(E.get())) { + TypeSourceInfo *TI = CE->getTypeInfoAsWritten(); + QualType T = Context.getCanonicalType(TI->getType()).getUnqualifiedType(); + if (T == Context.CharTy) + Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) + << NullValue + << FixItHint::CreateReplacement(E.get()->getExprLoc(), + NullValue ? "NULL" : "(void *)0"); + } } +} - // Handle compound assignments. - if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() != - OK_ObjCProperty) { - VK = VK_LValue; - OK = LHS.get()->getObjectKind(); +// C99 6.5.8, C++ [expr.rel] +QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + bool IsRelational = BinaryOperator::isRelationalOp(Opc); + bool IsThreeWay = Opc == BO_Cmp; + bool IsOrdered = IsRelational || IsThreeWay; + auto IsAnyPointerType = [](ExprResult E) { + QualType Ty = E.get()->getType(); + return Ty->isPointerType() || Ty->isMemberPointerType(); + }; + + // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer + // type, array-to-pointer, ..., conversions are performed on both operands to + // bring them to their composite type. + // Otherwise, all comparisons expect an rvalue, so convert to rvalue before + // any type-related checks. + if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + } else { + LHS = DefaultLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); } - // The LHS is not converted to the result type for fixed-point compound - // assignment as the common type is computed on demand. Reset the CompLHSTy - // to the LHS type we would have gotten after unary conversions. - if (CompResultTy->isFixedPointType()) - CompLHSTy = UsualUnaryConversions(LHS.get()).get()->getType(); + auto ConvertWideToRawPointer = [&](ExprResult E) -> ExprResult { + QualType Ty = E.get()->getType(); + auto *PT = Ty->getAs(); + if (!PT || PT->hasRawPointerLayout()) + return E; + return ImpCastExprToType(E.get(), + Context.getPointerType(PT->getPointeeType()), + CK_BoundsSafetyPointerCast); + }; - if (ConvertHalfVec) - return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true, - OpLoc, CurFPFeatureOverrides()); + LHS = ConvertWideToRawPointer(LHS); + RHS = ConvertWideToRawPointer(RHS); - return CompoundAssignOperator::Create( - Context, LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, - CurFPFeatureOverrides(), CompLHSTy, CompResultTy); -} + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/true); + if (!getLangOpts().CPlusPlus && BinaryOperator::isEqualityOp(Opc)) { + CheckPtrComparisonWithNullChar(LHS, RHS); + CheckPtrComparisonWithNullChar(RHS, LHS); + } -/// DiagnoseBitwisePrecedence - Emit a warning when bitwise and comparison -/// operators are mixed in a way that suggests that the programmer forgot that -/// comparison operators have higher precedence. The most typical example of -/// such code is "flags & 0x0020 != 0", which is equivalent to "flags & 1". -static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *LHSExpr, - Expr *RHSExpr) { - BinaryOperator *LHSBO = dyn_cast(LHSExpr); - BinaryOperator *RHSBO = dyn_cast(RHSExpr); + // Handle vector comparisons separately. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorCompareOperands(LHS, RHS, Loc, Opc); - // Check that one of the sides is a comparison operator and the other isn't. - bool isLeftComp = LHSBO && LHSBO->isComparisonOp(); - bool isRightComp = RHSBO && RHSBO->isComparisonOp(); - if (isLeftComp == isRightComp) - return; + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) + return CheckSizelessVectorCompareOperands(LHS, RHS, Loc, Opc); - // Bitwise operations are sometimes used as eager logical ops. - // Don't diagnose this. - bool isLeftBitwise = LHSBO && LHSBO->isBitwiseOp(); - bool isRightBitwise = RHSBO && RHSBO->isBitwiseOp(); - if (isLeftBitwise || isRightBitwise) - return; + diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); - SourceRange DiagRange = isLeftComp - ? SourceRange(LHSExpr->getBeginLoc(), OpLoc) - : SourceRange(OpLoc, RHSExpr->getEndLoc()); - StringRef OpStr = isLeftComp ? LHSBO->getOpcodeStr() : RHSBO->getOpcodeStr(); - SourceRange ParensRange = - isLeftComp - ? SourceRange(LHSBO->getRHS()->getBeginLoc(), RHSExpr->getEndLoc()) - : SourceRange(LHSExpr->getBeginLoc(), RHSBO->getLHS()->getEndLoc()); + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + if ((LHSType->isArithmeticType() || LHSType->isEnumeralType()) && + (RHSType->isArithmeticType() || RHSType->isEnumeralType())) + return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); - Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) - << DiagRange << BinaryOperator::getOpcodeStr(Opc) << OpStr; - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_silence) << OpStr, - (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange()); - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_bitwise_first) - << BinaryOperator::getOpcodeStr(Opc), - ParensRange); -} + if ((LHSType->isPointerType() && + LHSType->getPointeeType().isWebAssemblyReferenceType()) || + (RHSType->isPointerType() && + RHSType->getPointeeType().isWebAssemblyReferenceType())) + return InvalidOperands(Loc, LHS, RHS); -/// It accepts a '&&' expr that is inside a '||' one. -/// Emit a diagnostic together with a fixit hint that wraps the '&&' expression -/// in parentheses. -static void -EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, - BinaryOperator *Bop) { - assert(Bop->getOpcode() == BO_LAnd); - Self.Diag(Bop->getOperatorLoc(), diag::warn_logical_and_in_logical_or) - << Bop->getSourceRange() << OpLoc; - SuggestParentheses(Self, Bop->getOperatorLoc(), - Self.PDiag(diag::note_precedence_silence) - << Bop->getOpcodeStr(), - Bop->getSourceRange()); -} + const Expr::NullPointerConstantKind LHSNullKind = + LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + const Expr::NullPointerConstantKind RHSNullKind = + RHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull; + bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull; -/// Look for '&&' in the left hand of a '||' expr. -static void DiagnoseLogicalAndInLogicalOrLHS(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - if (BinaryOperator *Bop = dyn_cast(LHSExpr)) { - if (Bop->getOpcode() == BO_LAnd) { - // If it's "string_literal && a || b" don't warn since the precedence - // doesn't matter. - if (!isa(Bop->getLHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); - } else if (Bop->getOpcode() == BO_LOr) { - if (BinaryOperator *RBop = dyn_cast(Bop->getRHS())) { - // If it's "a || b && string_literal || c" we didn't warn earlier for - // "a || b && string_literal", but warn now. - if (RBop->getOpcode() == BO_LAnd && - isa(RBop->getRHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, RBop); - } - } - } -} + auto computeResultTy = [&]() { + if (Opc != BO_Cmp) + return Context.getLogicalOperationType(); + assert(getLangOpts().CPlusPlus); + assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); -/// Look for '&&' in the right hand of a '||' expr. -static void DiagnoseLogicalAndInLogicalOrRHS(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - if (BinaryOperator *Bop = dyn_cast(RHSExpr)) { - if (Bop->getOpcode() == BO_LAnd) { - // If it's "a || b && string_literal" don't warn since the precedence - // doesn't matter. - if (!isa(Bop->getRHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); - } - } -} + QualType CompositeTy = LHS.get()->getType(); + assert(!CompositeTy->isReferenceType()); -/// Look for bitwise op in the left or right hand of a bitwise op with -/// lower precedence and emit a diagnostic together with a fixit hint that wraps -/// the '&' expression in parentheses. -static void DiagnoseBitwiseOpInBitwiseOp(Sema &S, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *SubExpr) { - if (BinaryOperator *Bop = dyn_cast(SubExpr)) { - if (Bop->isBitwiseOp() && Bop->getOpcode() < Opc) { - S.Diag(Bop->getOperatorLoc(), diag::warn_bitwise_op_in_bitwise_op) - << Bop->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc) - << Bop->getSourceRange() << OpLoc; - SuggestParentheses(S, Bop->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) - << Bop->getOpcodeStr(), - Bop->getSourceRange()); - } - } -} + std::optional CCT = + getComparisonCategoryForBuiltinCmp(CompositeTy); + if (!CCT) + return InvalidOperands(Loc, LHS, RHS); -static void DiagnoseAdditionInShift(Sema &S, SourceLocation OpLoc, - Expr *SubExpr, StringRef Shift) { - if (BinaryOperator *Bop = dyn_cast(SubExpr)) { - if (Bop->getOpcode() == BO_Add || Bop->getOpcode() == BO_Sub) { - StringRef Op = Bop->getOpcodeStr(); - S.Diag(Bop->getOperatorLoc(), diag::warn_addition_in_bitshift) - << Bop->getSourceRange() << OpLoc << Shift << Op; - SuggestParentheses(S, Bop->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) << Op, - Bop->getSourceRange()); + if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) { + // P0946R0: Comparisons between a null pointer constant and an object + // pointer result in std::strong_equality, which is ill-formed under + // P1959R0. + Diag(Loc, diag::err_typecheck_three_way_comparison_of_pointer_and_zero) + << (LHSIsNull ? LHS.get()->getSourceRange() + : RHS.get()->getSourceRange()); + return QualType(); } - } -} - -static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - CXXOperatorCallExpr *OCE = dyn_cast(LHSExpr); - if (!OCE) - return; - - FunctionDecl *FD = OCE->getDirectCallee(); - if (!FD || !FD->isOverloadedOperator()) - return; - - OverloadedOperatorKind Kind = FD->getOverloadedOperator(); - if (Kind != OO_LessLess && Kind != OO_GreaterGreater) - return; - - S.Diag(OpLoc, diag::warn_overloaded_shift_in_comparison) - << LHSExpr->getSourceRange() << RHSExpr->getSourceRange() - << (Kind == OO_LessLess); - SuggestParentheses(S, OCE->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) - << (Kind == OO_LessLess ? "<<" : ">>"), - OCE->getSourceRange()); - SuggestParentheses( - S, OpLoc, S.PDiag(diag::note_evaluate_comparison_first), - SourceRange(OCE->getArg(1)->getBeginLoc(), RHSExpr->getEndLoc())); -} -/// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky -/// precedence. -static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *LHSExpr, - Expr *RHSExpr){ - // Diagnose "arg1 'bitwise' arg2 'eq' arg3". - if (BinaryOperator::isBitwiseOp(Opc)) - DiagnoseBitwisePrecedence(Self, Opc, OpLoc, LHSExpr, RHSExpr); + return CheckComparisonCategoryType( + *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); + }; - // Diagnose "arg1 & arg2 | arg3" - if ((Opc == BO_Or || Opc == BO_Xor) && - !OpLoc.isMacroID()/* Don't warn in macros. */) { - DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, LHSExpr); - DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, RHSExpr); + if (!IsOrdered && LHSIsNull != RHSIsNull) { + bool IsEquality = Opc == BO_EQ; + if (RHSIsNull) + DiagnoseAlwaysNonNullPointer(LHS.get(), RHSNullKind, IsEquality, + RHS.get()->getSourceRange()); + else + DiagnoseAlwaysNonNullPointer(RHS.get(), LHSNullKind, IsEquality, + LHS.get()->getSourceRange()); } - // Warn about arg1 || arg2 && arg3, as GCC 4.3+ does. - // We don't warn for 'assert(a || b && "bad")' since this is safe. - if (Opc == BO_LOr && !OpLoc.isMacroID()/* Don't warn in macros. */) { - DiagnoseLogicalAndInLogicalOrLHS(Self, OpLoc, LHSExpr, RHSExpr); - DiagnoseLogicalAndInLogicalOrRHS(Self, OpLoc, LHSExpr, RHSExpr); + if (IsOrdered && LHSType->isFunctionPointerType() && + RHSType->isFunctionPointerType()) { + // Valid unless a relational comparison of function pointers + bool IsError = Opc == BO_Cmp; + auto DiagID = + IsError ? diag::err_typecheck_ordered_comparison_of_function_pointers + : getLangOpts().CPlusPlus + ? diag::warn_typecheck_ordered_comparison_of_function_pointers + : diag::ext_typecheck_ordered_comparison_of_function_pointers; + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + if (IsError) + return QualType(); } - if ((Opc == BO_Shl && LHSExpr->getType()->isIntegralType(Self.getASTContext())) - || Opc == BO_Shr) { - StringRef Shift = BinaryOperator::getOpcodeStr(Opc); - DiagnoseAdditionInShift(Self, OpLoc, LHSExpr, Shift); - DiagnoseAdditionInShift(Self, OpLoc, RHSExpr, Shift); - } + if ((LHSType->isIntegerType() && !LHSIsNull) || + (RHSType->isIntegerType() && !RHSIsNull)) { + // Skip normal pointer conversion checks in this case; we have better + // diagnostics for this below. + } else if (getLangOpts().CPlusPlus) { + // Equality comparison of a function pointer to a void pointer is invalid, + // but we allow it as an extension. + // FIXME: If we really want to allow this, should it be part of composite + // pointer type computation so it works in conditionals too? + if (!IsOrdered && + ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) || + (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) { + // This is a gcc extension compatibility comparison. + // In a SFINAE context, we treat this as a hard error to maintain + // conformance with the C++ standard. + diagnoseFunctionPointerToVoidComparison( + *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); - // Warn on overloaded shift operators and comparisons, such as: - // cout << 5 == 4; - if (BinaryOperator::isComparisonOp(Opc)) - DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr); -} + if (isSFINAEContext()) + return QualType(); -ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, - tok::TokenKind Kind, - Expr *LHSExpr, Expr *RHSExpr) { - BinaryOperatorKind Opc = ConvertTokenKindToBinaryOpcode(Kind); - assert(LHSExpr && "ActOnBinOp(): missing left expression"); - assert(RHSExpr && "ActOnBinOp(): missing right expression"); - - // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" - DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); - - return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); -} - -void Sema::LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, - UnresolvedSetImpl &Functions) { - OverloadedOperatorKind OverOp = BinaryOperator::getOverloadedOperator(Opc); - if (OverOp != OO_None && OverOp != OO_Equal) - LookupOverloadedOperatorName(OverOp, S, Functions); - - // In C++20 onwards, we may have a second operator to look up. - if (getLangOpts().CPlusPlus20) { - if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp)) - LookupOverloadedOperatorName(ExtraOp, S, Functions); - } -} - -/// Build an overloaded binary operator expression in the given scope. -static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHS, Expr *RHS) { - switch (Opc) { - case BO_Assign: - // In the non-overloaded case, we warn about self-assignment (x = x) for - // both simple assignment and certain compound assignments where algebra - // tells us the operation yields a constant result. When the operator is - // overloaded, we can't do the latter because we don't want to assume that - // those algebraic identities still apply; for example, a path-building - // library might use operator/= to append paths. But it's still reasonable - // to assume that simple assignment is just moving/copying values around - // and so self-assignment is likely a bug. - DiagnoseSelfAssignment(S, LHS, RHS, OpLoc, false); - [[fallthrough]]; - case BO_DivAssign: - case BO_RemAssign: - case BO_SubAssign: - case BO_AndAssign: - case BO_OrAssign: - case BO_XorAssign: - CheckIdentityFieldAssignment(LHS, RHS, OpLoc, S); - break; - default: - break; - } - - // Find all of the overloaded operators visible from this point. - UnresolvedSet<16> Functions; - S.LookupBinOp(Sc, OpLoc, Opc, Functions); - - // Build the (potentially-overloaded, potentially-dependent) - // binary operation. - return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); -} - -ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { - ExprResult LHS, RHS; - std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); - if (!LHS.isUsable() || !RHS.isUsable()) - return ExprError(); - LHSExpr = LHS.get(); - RHSExpr = RHS.get(); - - // We want to end up calling one of SemaPseudoObject::checkAssignment - // (if the LHS is a pseudo-object), BuildOverloadedBinOp (if - // both expressions are overloadable or either is type-dependent), - // or CreateBuiltinBinOp (in any other case). We also want to get - // any placeholder types out of the way. - - // Handle pseudo-objects in the LHS. - if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { - // Assignments with a pseudo-object l-value need special analysis. - if (pty->getKind() == BuiltinType::PseudoObject && - BinaryOperator::isAssignmentOp(Opc)) - return PseudoObject().checkAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); - - // Don't resolve overloads if the other type is overloadable. - if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload) { - // We can't actually test that if we still have a placeholder, - // though. Fortunately, none of the exceptions we see in that - // code below are valid when the LHS is an overload set. Note - // that an overload set can be dependently-typed, but it never - // instantiates to having an overloadable type. - ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); - if (resolvedRHS.isInvalid()) return ExprError(); - RHSExpr = resolvedRHS.get(); + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); + } - if (RHSExpr->isTypeDependent() || - RHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + // C++ [expr.eq]p2: + // If at least one operand is a pointer [...] bring them to their + // composite pointer type. + // C++ [expr.spaceship]p6 + // If at least one of the operands is of pointer type, [...] bring them + // to their composite pointer type. + // C++ [expr.rel]p2: + // If both operands are pointers, [...] bring them to their composite + // pointer type. + // For <=>, the only valid non-pointer types are arrays and functions, and + // we already decayed those, so this is really the same as the relational + // comparison rule. + if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= + (IsOrdered ? 2 : 1) && + (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || + RHSType->isObjCObjectPointerType()))) { + if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) + return QualType(); + return computeResultTy(); } + } else if (LHSType->isPointerType() && + RHSType->isPointerType()) { // C99 6.5.8p2 + // All of the following pointer-related warnings are GCC extensions, except + // when handling null pointer constants. + QualType LCanPointeeTy = + LHSType->castAs()->getPointeeType().getCanonicalType(); + QualType RCanPointeeTy = + RHSType->castAs()->getPointeeType().getCanonicalType(); - // If we're instantiating "a.x < b" or "A::x < b" and 'x' names a function - // template, diagnose the missing 'template' keyword instead of diagnosing - // an invalid use of a bound member function. - // - // Note that "A::x < b" might be valid if 'b' has an overloadable type due - // to C++1z [over.over]/1.4, but we already checked for that case above. - if (Opc == BO_LT && inTemplateInstantiation() && - (pty->getKind() == BuiltinType::BoundMember || - pty->getKind() == BuiltinType::Overload)) { - auto *OE = dyn_cast(LHSExpr); - if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() && - llvm::any_of(OE->decls(), [](NamedDecl *ND) { - return isa(ND); - })) { - Diag(OE->getQualifier() ? OE->getQualifierLoc().getBeginLoc() - : OE->getNameLoc(), - diag::err_template_kw_missing) - << OE->getName().getAsString() << ""; - return ExprError(); + // C99 6.5.9p2 and C99 6.5.8p2 + if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), + RCanPointeeTy.getUnqualifiedType())) { + if (IsRelational) { + // Pointers both need to point to complete or incomplete types + if ((LCanPointeeTy->isIncompleteType() != + RCanPointeeTy->isIncompleteType()) && + !getLangOpts().C11) { + Diag(Loc, diag::ext_typecheck_compare_complete_incomplete_pointers) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange() + << LHSType << RHSType << LCanPointeeTy->isIncompleteType() + << RCanPointeeTy->isIncompleteType(); + } + } + } else if (!IsRelational && + (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) { + // Valid unless comparison between non-null pointer and function pointer + if ((LCanPointeeTy->isFunctionType() || RCanPointeeTy->isFunctionType()) + && !LHSIsNull && !RHSIsNull) + diagnoseFunctionPointerToVoidComparison(*this, Loc, LHS, RHS, + /*isError*/false); + } else { + // Invalid + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); + } + if (LCanPointeeTy != RCanPointeeTy) { + // Treat NULL constant as a special case in OpenCL. + if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { + if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy)) { + Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSType << RHSType << 0 /* comparison */ + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } } + LangAS AddrSpaceL = LCanPointeeTy.getAddressSpace(); + LangAS AddrSpaceR = RCanPointeeTy.getAddressSpace(); + CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion + : CK_BitCast; + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, Kind); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); } - - ExprResult LHS = CheckPlaceholderExpr(LHSExpr); - if (LHS.isInvalid()) return ExprError(); - LHSExpr = LHS.get(); + return computeResultTy(); } - // Handle pseudo-objects in the RHS. - if (const BuiltinType *pty = RHSExpr->getType()->getAsPlaceholderType()) { - // An overload in the RHS can potentially be resolved by the type - // being assigned to. - if (Opc == BO_Assign && pty->getKind() == BuiltinType::Overload) { - if (getLangOpts().CPlusPlus && - (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || - LHSExpr->getType()->isOverloadableType())) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + // C++ [expr.eq]p4: + // Two operands of type std::nullptr_t or one operand of type + // std::nullptr_t and the other a null pointer constant compare + // equal. + // C23 6.5.9p5: + // If both operands have type nullptr_t or one operand has type nullptr_t + // and the other is a null pointer constant, they compare equal if the + // former is a null pointer. + if (!IsOrdered && LHSIsNull && RHSIsNull) { + if (LHSType->isNullPtrType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (RHSType->isNullPtrType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); } + } - // Don't resolve overloads if the other type is overloadable. - if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload && - LHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + if (!getLangOpts().CPlusPlus && !IsOrdered && (LHSIsNull || RHSIsNull)) { + // C23 6.5.9p6: + // Otherwise, at least one operand is a pointer. If one is a pointer and + // the other is a null pointer constant or has type nullptr_t, they + // compare equal + if (LHSIsNull && RHSType->isPointerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + if (RHSIsNull && LHSType->isPointerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + } - ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); - if (!resolvedRHS.isUsable()) return ExprError(); - RHSExpr = resolvedRHS.get(); + // Comparison of Objective-C pointers and block pointers against nullptr_t. + // These aren't covered by the composite pointer type rules. + if (!IsOrdered && RHSType->isNullPtrType() && + (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (!IsOrdered && LHSType->isNullPtrType() && + (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); } if (getLangOpts().CPlusPlus) { - // Otherwise, build an overloaded op if either expression is type-dependent - // or has an overloadable type. - if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || - LHSExpr->getType()->isOverloadableType() || - RHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + if (IsRelational && + ((LHSType->isNullPtrType() && RHSType->isPointerType()) || + (RHSType->isNullPtrType() && LHSType->isPointerType()))) { + // HACK: Relational comparison of nullptr_t against a pointer type is + // invalid per DR583, but we allow it within std::less<> and friends, + // since otherwise common uses of it break. + // FIXME: Consider removing this hack once LWG fixes std::less<> and + // friends to have std::nullptr_t overload candidates. + DeclContext *DC = CurContext; + if (isa(DC)) + DC = DC->getParent(); + if (auto *CTSD = dyn_cast(DC)) { + if (CTSD->isInStdNamespace() && + llvm::StringSwitch(CTSD->getName()) + .Cases("less", "less_equal", "greater", "greater_equal", true) + .Default(false)) { + if (RHSType->isNullPtrType()) + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + else + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + } + } + + // C++ [expr.eq]p2: + // If at least one operand is a pointer to member, [...] bring them to + // their composite pointer type. + if (!IsOrdered && + (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { + if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) + return QualType(); + else + return computeResultTy(); + } } - if (getLangOpts().RecoveryAST && - (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { - assert(!getLangOpts().CPlusPlus); - assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && - "Should only occur in error-recovery path."); - if (BinaryOperator::isCompoundAssignmentOp(Opc)) - // C [6.15.16] p3: - // An assignment expression has the value of the left operand after the - // assignment, but is not an lvalue. - return CompoundAssignOperator::Create( - Context, LHSExpr, RHSExpr, Opc, - LHSExpr->getType().getUnqualifiedType(), VK_PRValue, OK_Ordinary, - OpLoc, CurFPFeatureOverrides()); - QualType ResultType; - switch (Opc) { - case BO_Assign: - ResultType = LHSExpr->getType().getUnqualifiedType(); - break; - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - case BO_LAnd: - case BO_LOr: - // These operators have a fixed result type regardless of operands. - ResultType = Context.IntTy; - break; - case BO_Comma: - ResultType = RHSExpr->getType(); - break; - default: - ResultType = Context.DependentTy; - break; + // Handle block pointer types. + if (!IsOrdered && LHSType->isBlockPointerType() && + RHSType->isBlockPointerType()) { + QualType lpointee = LHSType->castAs()->getPointeeType(); + QualType rpointee = RHSType->castAs()->getPointeeType(); + + if (!LHSIsNull && !RHSIsNull && + !Context.typesAreCompatible(lpointee, rpointee)) { + Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); } - return BinaryOperator::Create(Context, LHSExpr, RHSExpr, Opc, ResultType, - VK_PRValue, OK_Ordinary, OpLoc, - CurFPFeatureOverrides()); + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); } - // Build a built-in binary operation. - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); -} + // Allow block pointers to be compared with null pointer constants. + if (!IsOrdered + && ((LHSType->isBlockPointerType() && RHSType->isPointerType()) + || (LHSType->isPointerType() && RHSType->isBlockPointerType()))) { + if (!LHSIsNull && !RHSIsNull) { + if (!((RHSType->isPointerType() && RHSType->castAs() + ->getPointeeType()->isVoidType()) + || (LHSType->isPointerType() && LHSType->castAs() + ->getPointeeType()->isVoidType()))) + Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + } + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, + RHSType->isPointerType() ? CK_BitCast + : CK_AnyPointerToBlockPointerCast); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, + LHSType->isPointerType() ? CK_BitCast + : CK_AnyPointerToBlockPointerCast); + return computeResultTy(); + } -static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { - if (T.isNull() || T->isDependentType()) - return false; + if (LHSType->isObjCObjectPointerType() || + RHSType->isObjCObjectPointerType()) { + const PointerType *LPT = LHSType->getAs(); + const PointerType *RPT = RHSType->getAs(); + if (LPT || RPT) { + bool LPtrToVoid = LPT ? LPT->getPointeeType()->isVoidType() : false; + bool RPtrToVoid = RPT ? RPT->getPointeeType()->isVoidType() : false; - if (!Ctx.isPromotableIntegerType(T)) - return true; + if (!LPtrToVoid && !RPtrToVoid && + !Context.typesAreCompatible(LHSType, RHSType)) { + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, + /*isError*/false); + } + // FIXME: If LPtrToVoid, we should presumably convert the LHS rather than + // the RHS, but we have test coverage for this behavior. + // FIXME: Consider using convertPointersToCompositeType in C++. + if (LHSIsNull && !RHSIsNull) { + Expr *E = LHS.get(); + if (getLangOpts().ObjCAutoRefCount) + ObjC().CheckObjCConversion(SourceRange(), RHSType, E, + CheckedConversionKind::Implicit); + LHS = ImpCastExprToType(E, RHSType, + RPT ? CK_BitCast :CK_CPointerToObjCPointerCast); + } + else { + Expr *E = RHS.get(); + if (getLangOpts().ObjCAutoRefCount) + ObjC().CheckObjCConversion(SourceRange(), LHSType, E, + CheckedConversionKind::Implicit, + /*Diagnose=*/true, + /*DiagnoseCFAudited=*/false, Opc); + RHS = ImpCastExprToType(E, LHSType, + LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); + } + return computeResultTy(); + } + if (LHSType->isObjCObjectPointerType() && + RHSType->isObjCObjectPointerType()) { + if (!Context.areComparableObjCPointerTypes(LHSType, RHSType)) + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, + /*isError*/false); + if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS)) + diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc); - return Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); -} + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); + } -ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, - UnaryOperatorKind Opc, Expr *InputExpr, - bool IsAfterAmp) { - ExprResult Input = InputExpr; - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - QualType resultType; - bool CanOverflow = false; + if (!IsOrdered && LHSType->isBlockPointerType() && + RHSType->isBlockCompatibleObjCPointerType(Context)) { + LHS = ImpCastExprToType(LHS.get(), RHSType, + CK_BlockPointerToObjCPointerCast); + return computeResultTy(); + } else if (!IsOrdered && + LHSType->isBlockCompatibleObjCPointerType(Context) && + RHSType->isBlockPointerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, + CK_BlockPointerToObjCPointerCast); + return computeResultTy(); + } + } + if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || + (LHSType->isIntegerType() && RHSType->isAnyPointerType())) { + unsigned DiagID = 0; + bool isError = false; + if (LangOpts.DebuggerSupport) { + // Under a debugger, allow the comparison of pointers to integers, + // since users tend to want to compare addresses. + } else if ((LHSIsNull && LHSType->isIntegerType()) || + (RHSIsNull && RHSType->isIntegerType())) { + if (IsOrdered) { + isError = getLangOpts().CPlusPlus; + DiagID = + isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero + : diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; + } + } else if (getLangOpts().CPlusPlus) { + DiagID = diag::err_typecheck_comparison_of_pointer_integer; + isError = true; + } else if (IsOrdered) + DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; + else + DiagID = diag::ext_typecheck_comparison_of_pointer_integer; - bool ConvertHalfVec = false; - if (getLangOpts().OpenCL) { - QualType Ty = InputExpr->getType(); - // The only legal unary operation for atomics is '&'. - if ((Opc != UO_AddrOf && Ty->isAtomicType()) || - // OpenCL special types - image, sampler, pipe, and blocks are to be used - // only with a builtin functions and therefore should be disallowed here. - (Ty->isImageType() || Ty->isSamplerT() || Ty->isPipeType() - || Ty->isBlockPointerType())) { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << InputExpr->getType() - << Input.get()->getSourceRange()); + if (DiagID) { + Diag(Loc, DiagID) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + if (isError) + return QualType(); } + + if (LHSType->isIntegerType()) + LHS = ImpCastExprToType(LHS.get(), RHSType, + LHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, + RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); + return computeResultTy(); } - if (getLangOpts().HLSL && OpLoc.isValid()) { - if (Opc == UO_AddrOf) - return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 0); - if (Opc == UO_Deref) - return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1); + // Handle block pointers. + if (!IsOrdered && RHSIsNull + && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (!IsOrdered && LHSIsNull + && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); } - if (InputExpr->isTypeDependent() && - InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) { - resultType = Context.DependentTy; - } else { - switch (Opc) { - case UO_PreInc: - case UO_PreDec: - case UO_PostInc: - case UO_PostDec: - resultType = - CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, - Opc == UO_PreInc || Opc == UO_PostInc, - Opc == UO_PreInc || Opc == UO_PreDec); - CanOverflow = isOverflowingIntegerType(Context, resultType); - break; - case UO_AddrOf: - resultType = CheckAddressOfOperand(Input, OpLoc); - CheckAddressOfNoDeref(InputExpr); - RecordModifiableNonNullParam(*this, InputExpr); - break; - case UO_Deref: { - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = - CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); - break; + if (getLangOpts().getOpenCLCompatibleVersion() >= 200) { + if (LHSType->isClkEventT() && RHSType->isClkEventT()) { + return computeResultTy(); } - case UO_Plus: - case UO_Minus: - CanOverflow = Opc == UO_Minus && - isOverflowingIntegerType(Context, Input.get()->getType()); - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - // Unary plus and minus require promoting an operand of half vector to a - // float vector and truncating the result back to a half vector. For now, - // we do this only when HalfArgsAndReturns is set (that is, when the - // target is arm or arm64). - ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); - - // If the operand is a half vector, promote it to a float vector. - if (ConvertHalfVec) - Input = convertVector(Input.get(), Context.FloatTy, *this); - resultType = Input.get()->getType(); - if (resultType->isArithmeticType()) // C99 6.5.3.3p1 - break; - else if (resultType->isVectorType() && - // The z vector extensions don't allow + or - with bool vectors. - (!Context.getLangOpts().ZVector || - resultType->castAs()->getVectorKind() != - VectorKind::AltiVecBool)) - break; - else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - - break; - else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 - Opc == UO_Plus && resultType->isPointerType()) - break; - - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - - case UO_Not: // bitwise complement - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. - if (resultType->isComplexType() || resultType->isComplexIntegerType()) - // C99 does not support '~' for complex conjugation. - Diag(OpLoc, diag::ext_integer_complement_complex) - << resultType << Input.get()->getSourceRange(); - else if (resultType->hasIntegerRepresentation()) - break; - else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { - // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate - // on vector float types. - QualType T = resultType->castAs()->getElementType(); - if (!T->isIntegerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - break; - - case UO_LNot: // logical negation - // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - - // Though we still have to promote half FP to float... - if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { - Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) - .get(); - resultType = Context.FloatTy; - } - - // WebAsembly tables can't be used in unary expressions. - if (resultType->isPointerType() && - resultType->getPointeeType().isWebAssemblyReferenceType()) { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { - // C99 6.5.3.3p1: ok, fallthrough; - if (Context.getLangOpts().CPlusPlus) { - // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: - // operand contextually converted to bool. - Input = ImpCastExprToType(Input.get(), Context.BoolTy, - ScalarTypeToBooleanCastKind(resultType)); - } else if (Context.getLangOpts().OpenCL && - Context.getLangOpts().OpenCLVersion < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on scalar float types. - if (!resultType->isIntegerType() && !resultType->isPointerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - } else if (resultType->isExtVectorType()) { - if (Context.getLangOpts().OpenCL && - Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on vector float types. - QualType T = resultType->castAs()->getElementType(); - if (!T->isIntegerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); - break; - } else if (Context.getLangOpts().CPlusPlus && - resultType->isVectorType()) { - const VectorType *VTy = resultType->castAs(); - if (VTy->getVectorKind() != VectorKind::Generic) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); + if (LHSType->isQueueT() && RHSType->isQueueT()) { + return computeResultTy(); + } - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); - break; - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } + if (LHSIsNull && RHSType->isQueueT()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } - // LNot always has type int. C99 6.5.3.3p5. - // In C++, it's bool. C++ 5.3.1p8 - resultType = Context.getLogicalOperationType(); - break; - case UO_Real: - case UO_Imag: - resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); - // _Real maps ordinary l-values into ordinary l-values. _Imag maps - // ordinary complex l-values to ordinary l-values and all other values to - // r-values. - if (Input.isInvalid()) - return ExprError(); - if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { - if (Input.get()->isGLValue() && - Input.get()->getObjectKind() == OK_Ordinary) - VK = Input.get()->getValueKind(); - } else if (!getLangOpts().CPlusPlus) { - // In C, a volatile scalar is read by __imag. In C++, it is not. - Input = DefaultLvalueConversion(Input.get()); - } - break; - case UO_Extension: - resultType = Input.get()->getType(); - VK = Input.get()->getValueKind(); - OK = Input.get()->getObjectKind(); - break; - case UO_Coawait: - // It's unnecessary to represent the pass-through operator co_await in the - // AST; just return the input expression instead. - assert(!Input.get()->getType()->isDependentType() && - "the co_await expression must be non-dependant before " - "building operator co_await"); - return Input; + if (LHSType->isQueueT() && RHSIsNull) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); } } - if (resultType.isNull() || Input.isInvalid()) - return ExprError(); - // Check for array bounds violations in the operand of the UnaryOperator, - // except for the '*' and '&' operators that have to be handled specially - // by CheckArrayAccess (as there are special cases like &array[arraysize] - // that are explicitly defined as valid by the standard). - if (Opc != UO_AddrOf && Opc != UO_Deref) - CheckArrayAccess(Input.get()); + return InvalidOperands(Loc, LHS, RHS); +} - auto *UO = - UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK, - OpLoc, CanOverflow, CurFPFeatureOverrides()); +QualType Sema::GetSignedVectorType(QualType V) { + const VectorType *VTy = V->castAs(); + unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); - if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && - !isa(UO->getType().getDesugaredType(Context)) && - !isUnevaluatedContext()) - ExprEvalContexts.back().PossibleDerefs.insert(UO); + if (isa(VTy)) { + if (VTy->isExtVectorBoolType()) + return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.CharTy)) + return Context.getExtVectorType(Context.CharTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.ShortTy)) + return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.IntTy)) + return Context.getExtVectorType(Context.IntTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.Int128Ty)) + return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.LongTy)) + return Context.getExtVectorType(Context.LongTy, VTy->getNumElements()); + assert(TypeSize == Context.getTypeSize(Context.LongLongTy) && + "Unhandled vector element size in vector compare"); + return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements()); + } - // Convert the result back to a half vector. - if (ConvertHalfVec) - return convertVector(UO, Context.HalfTy, *this); - return UO; + if (TypeSize == Context.getTypeSize(Context.Int128Ty)) + return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.LongLongTy)) + return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.LongTy)) + return Context.getVectorType(Context.LongTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.IntTy)) + return Context.getVectorType(Context.IntTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.ShortTy)) + return Context.getVectorType(Context.ShortTy, VTy->getNumElements(), + VectorKind::Generic); + assert(TypeSize == Context.getTypeSize(Context.CharTy) && + "Unhandled vector element size in vector compare"); + return Context.getVectorType(Context.CharTy, VTy->getNumElements(), + VectorKind::Generic); } -bool Sema::isQualifiedMemberAccess(Expr *E) { - if (DeclRefExpr *DRE = dyn_cast(E)) { - if (!DRE->getQualifier()) - return false; +QualType Sema::GetSignedSizelessVectorType(QualType V) { + const BuiltinType *VTy = V->castAs(); + assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); - ValueDecl *VD = DRE->getDecl(); - if (!VD->isCXXClassMember()) - return false; + const QualType ETy = V->getSveEltType(Context); + const auto TypeSize = Context.getTypeSize(ETy); - if (isa(VD) || isa(VD)) - return true; - if (CXXMethodDecl *Method = dyn_cast(VD)) - return Method->isImplicitObjectMemberFunction(); + const QualType IntTy = Context.getIntTypeForBitwidth(TypeSize, true); + const llvm::ElementCount VecSize = Context.getBuiltinVectorTypeInfo(VTy).EC; + return Context.getScalableVectorType(IntTy, VecSize.getKnownMinValue()); +} - return false; +QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) { + Diag(Loc, diag::err_three_way_vector_comparison); + return QualType(); } - if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { - if (!ULE->getQualifier()) - return false; + // Check to make sure we're operating on vectors of the same type and width, + // Allowing one side to be a scalar of element type. + QualType vType = + CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ true, + /*ReportInvalid*/ true); + if (vType.isNull()) + return vType; - for (NamedDecl *D : ULE->decls()) { - if (CXXMethodDecl *Method = dyn_cast(D)) { - if (Method->isImplicitObjectMemberFunction()) - return true; - } else { - // Overload set does not contain methods. - break; - } - } + QualType LHSType = LHS.get()->getType(); - return false; + // Determine the return type of a vector compare. By default clang will return + // a scalar for all vector compares except vector bool and vector pixel. + // With the gcc compiler we will always return a vector type and with the xl + // compiler we will always return a scalar type. This switch allows choosing + // which behavior is prefered. + if (getLangOpts().AltiVec) { + switch (getLangOpts().getAltivecSrcCompat()) { + case LangOptions::AltivecSrcCompatKind::Mixed: + // If AltiVec, the comparison results in a numeric type, i.e. + // bool for C++, int for C + if (vType->castAs()->getVectorKind() == + VectorKind::AltiVecVector) + return Context.getLogicalOperationType(); + else + Diag(Loc, diag::warn_deprecated_altivec_src_compat); + break; + case LangOptions::AltivecSrcCompatKind::GCC: + // For GCC we always return the vector type. + break; + case LangOptions::AltivecSrcCompatKind::XL: + return Context.getLogicalOperationType(); + break; + } } - return false; -} - -ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc, - UnaryOperatorKind Opc, Expr *Input, - bool IsAfterAmp) { - // First things first: handle placeholders so that the - // overloaded-operator check considers the right type. - if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { - // Increment and decrement of pseudo-object references. - if (pty->getKind() == BuiltinType::PseudoObject && - UnaryOperator::isIncrementDecrementOp(Opc)) - return PseudoObject().checkIncDec(S, OpLoc, Opc, Input); - - // extension is always a builtin operator. - if (Opc == UO_Extension) - return CreateBuiltinUnaryOp(OpLoc, Opc, Input); - - // & gets special logic for several kinds of placeholder. - // The builtin code knows what to do. - if (Opc == UO_AddrOf && - (pty->getKind() == BuiltinType::Overload || - pty->getKind() == BuiltinType::UnknownAny || - pty->getKind() == BuiltinType::BoundMember)) - return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); - // Anything else needs to be handled now. - ExprResult Result = CheckPlaceholderExpr(Input); - if (Result.isInvalid()) return ExprError(); - Input = Result.get(); + // Check for comparisons of floating point operands using != and ==. + if (LHSType->hasFloatingRepresentation()) { + assert(RHS.get()->getType()->hasFloatingRepresentation()); + CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); } - if (getLangOpts().CPlusPlus && Input->getType()->isOverloadableType() && - UnaryOperator::getOverloadedOperator(Opc) != OO_None && - !(Opc == UO_AddrOf && isQualifiedMemberAccess(Input))) { - // Find all of the overloaded operators visible from this point. - UnresolvedSet<16> Functions; - OverloadedOperatorKind OverOp = UnaryOperator::getOverloadedOperator(Opc); - if (S && OverOp != OO_None) - LookupOverloadedOperatorName(OverOp, S, Functions); + // Return a signed type for the vector. + return GetSignedVectorType(vType); +} - return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input); +QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) { + Diag(Loc, diag::err_three_way_vector_comparison); + return QualType(); } - return CreateBuiltinUnaryOp(OpLoc, Opc, Input, IsAfterAmp); -} - -ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, - Expr *Input, bool IsAfterAmp) { - return BuildUnaryOp(S, OpLoc, ConvertTokenKindToUnaryOpcode(Op), Input, - IsAfterAmp); -} + // Check to make sure we're operating on vectors of the same type and width, + // Allowing one side to be a scalar of element type. + QualType vType = CheckSizelessVectorOperands( + LHS, RHS, Loc, /*isCompAssign*/ false, ACK_Comparison); -ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, - LabelDecl *TheDecl) { - TheDecl->markUsed(Context); - // Create the AST node. The address of a label always has type 'void*'. - auto *Res = new (Context) AddrLabelExpr( - OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); + if (vType.isNull()) + return vType; - if (getCurFunction()) - getCurFunction()->AddrLabels.push_back(Res); + QualType LHSType = LHS.get()->getType(); - return Res; -} + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); -void Sema::ActOnStartStmtExpr() { - PushExpressionEvaluationContext(ExprEvalContexts.back().Context); - // Make sure we diagnose jumping into a statement expression. - setFunctionHasBranchProtectedScope(); -} + // Check for comparisons of floating point operands using != and ==. + if (LHSType->hasFloatingRepresentation()) { + assert(RHS.get()->getType()->hasFloatingRepresentation()); + CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + } -void Sema::ActOnStmtExprError() { - // Note that function is also called by TreeTransform when leaving a - // StmtExpr scope without rebuilding anything. + const BuiltinType *LHSBuiltinTy = LHSType->getAs(); + const BuiltinType *RHSBuiltinTy = RHS.get()->getType()->getAs(); - DiscardCleanupsInEvaluationContext(); - PopExpressionEvaluationContext(); -} + if (LHSBuiltinTy && RHSBuiltinTy && LHSBuiltinTy->isSVEBool() && + RHSBuiltinTy->isSVEBool()) + return LHSType; -ExprResult Sema::ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc) { - return BuildStmtExpr(LPLoc, SubStmt, RPLoc, getTemplateDepth(S)); + // Return a signed type for the vector. + return GetSignedSizelessVectorType(vType); } -ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc, unsigned TemplateDepth) { - assert(SubStmt && isa(SubStmt) && "Invalid action invocation!"); - CompoundStmt *Compound = cast(SubStmt); +static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS, + const ExprResult &XorRHS, + const SourceLocation Loc) { + // Do not diagnose macros. + if (Loc.isMacroID()) + return; - if (hasAnyUnrecoverableErrorsInThisFunction()) - DiscardCleanupsInEvaluationContext(); - assert(!Cleanup.exprNeedsCleanups() && - "cleanups within StmtExpr not correctly bound!"); - PopExpressionEvaluationContext(); + // Do not diagnose if both LHS and RHS are macros. + if (XorLHS.get()->getExprLoc().isMacroID() && + XorRHS.get()->getExprLoc().isMacroID()) + return; - // FIXME: there are a variety of strange constraints to enforce here, for - // example, it is not possible to goto into a stmt expression apparently. - // More semantic analysis is needed. + bool Negative = false; + bool ExplicitPlus = false; + const auto *LHSInt = dyn_cast(XorLHS.get()); + const auto *RHSInt = dyn_cast(XorRHS.get()); - // If there are sub-stmts in the compound stmt, take the type of the last one - // as the type of the stmtexpr. - QualType Ty = Context.VoidTy; - bool StmtExprMayBindToTemp = false; - if (!Compound->body_empty()) { - // For GCC compatibility we get the last Stmt excluding trailing NullStmts. - if (const auto *LastStmt = - dyn_cast(Compound->getStmtExprResult())) { - if (const Expr *Value = LastStmt->getExprStmt()) { - StmtExprMayBindToTemp = true; - Ty = Value->getType(); - } + if (!LHSInt) + return; + if (!RHSInt) { + // Check negative literals. + if (const auto *UO = dyn_cast(XorRHS.get())) { + UnaryOperatorKind Opc = UO->getOpcode(); + if (Opc != UO_Minus && Opc != UO_Plus) + return; + RHSInt = dyn_cast(UO->getSubExpr()); + if (!RHSInt) + return; + Negative = (Opc == UO_Minus); + ExplicitPlus = !Negative; + } else { + return; } } - // FIXME: Check that expression type is complete/non-abstract; statement - // expressions are not lvalues. - Expr *ResStmtExpr = - new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth); - if (StmtExprMayBindToTemp) - return MaybeBindToTemporary(ResStmtExpr); - return ResStmtExpr; -} - -ExprResult Sema::ActOnStmtExprResult(ExprResult ER) { - if (ER.isInvalid()) - return ExprError(); + const llvm::APInt &LeftSideValue = LHSInt->getValue(); + llvm::APInt RightSideValue = RHSInt->getValue(); + if (LeftSideValue != 2 && LeftSideValue != 10) + return; - // Do function/array conversion on the last expression, but not - // lvalue-to-rvalue. However, initialize an unqualified type. - ER = DefaultFunctionArrayConversion(ER.get()); - if (ER.isInvalid()) - return ExprError(); - Expr *E = ER.get(); + if (LeftSideValue.getBitWidth() != RightSideValue.getBitWidth()) + return; - if (E->isTypeDependent()) - return E; + CharSourceRange ExprRange = CharSourceRange::getCharRange( + LHSInt->getBeginLoc(), S.getLocForEndOfToken(RHSInt->getLocation())); + llvm::StringRef ExprStr = + Lexer::getSourceText(ExprRange, S.getSourceManager(), S.getLangOpts()); - // In ARC, if the final expression ends in a consume, splice - // the consume out and bind it later. In the alternate case - // (when dealing with a retainable type), the result - // initialization will create a produce. In both cases the - // result will be +1, and we'll need to balance that out with - // a bind. - auto *Cast = dyn_cast(E); - if (Cast && Cast->getCastKind() == CK_ARCConsumeObject) - return Cast->getSubExpr(); + CharSourceRange XorRange = + CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + llvm::StringRef XorStr = + Lexer::getSourceText(XorRange, S.getSourceManager(), S.getLangOpts()); + // Do not diagnose if xor keyword/macro is used. + if (XorStr == "xor") + return; - // FIXME: Provide a better location for the initialization. - return PerformCopyInitialization( - InitializedEntity::InitializeStmtExprResult( - E->getBeginLoc(), E->getType().getUnqualifiedType()), - SourceLocation(), E); -} + std::string LHSStr = std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(LHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts())); + std::string RHSStr = std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(RHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts())); -ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, - TypeSourceInfo *TInfo, - ArrayRef Components, - SourceLocation RParenLoc) { - QualType ArgTy = TInfo->getType(); - bool Dependent = ArgTy->isDependentType(); - SourceRange TypeRange = TInfo->getTypeLoc().getLocalSourceRange(); + if (Negative) { + RightSideValue = -RightSideValue; + RHSStr = "-" + RHSStr; + } else if (ExplicitPlus) { + RHSStr = "+" + RHSStr; + } - // We must have at least one component that refers to the type, and the first - // one is known to be a field designator. Verify that the ArgTy represents - // a struct/union/class. - if (!Dependent && !ArgTy->isRecordType()) - return ExprError(Diag(BuiltinLoc, diag::err_offsetof_record_type) - << ArgTy << TypeRange); + StringRef LHSStrRef = LHSStr; + StringRef RHSStrRef = RHSStr; + // Do not diagnose literals with digit separators, binary, hexadecimal, octal + // literals. + if (LHSStrRef.starts_with("0b") || LHSStrRef.starts_with("0B") || + RHSStrRef.starts_with("0b") || RHSStrRef.starts_with("0B") || + LHSStrRef.starts_with("0x") || LHSStrRef.starts_with("0X") || + RHSStrRef.starts_with("0x") || RHSStrRef.starts_with("0X") || + (LHSStrRef.size() > 1 && LHSStrRef.starts_with("0")) || + (RHSStrRef.size() > 1 && RHSStrRef.starts_with("0")) || + LHSStrRef.contains('\'') || RHSStrRef.contains('\'')) + return; - // Type must be complete per C99 7.17p3 because a declaring a variable - // with an incomplete type would be ill-formed. - if (!Dependent - && RequireCompleteType(BuiltinLoc, ArgTy, - diag::err_offsetof_incomplete_type, TypeRange)) - return ExprError(); + bool SuggestXor = + S.getLangOpts().CPlusPlus || S.getPreprocessor().isMacroDefined("xor"); + const llvm::APInt XorValue = LeftSideValue ^ RightSideValue; + int64_t RightSideIntValue = RightSideValue.getSExtValue(); + if (LeftSideValue == 2 && RightSideIntValue >= 0) { + std::string SuggestedExpr = "1 << " + RHSStr; + bool Overflow = false; + llvm::APInt One = (LeftSideValue - 1); + llvm::APInt PowValue = One.sshl_ov(RightSideValue, Overflow); + if (Overflow) { + if (RightSideIntValue < 64) + S.Diag(Loc, diag::warn_xor_used_as_pow_base) + << ExprStr << toString(XorValue, 10, true) << ("1LL << " + RHSStr) + << FixItHint::CreateReplacement(ExprRange, "1LL << " + RHSStr); + else if (RightSideIntValue == 64) + S.Diag(Loc, diag::warn_xor_used_as_pow) + << ExprStr << toString(XorValue, 10, true); + else + return; + } else { + S.Diag(Loc, diag::warn_xor_used_as_pow_base_extra) + << ExprStr << toString(XorValue, 10, true) << SuggestedExpr + << toString(PowValue, 10, true) + << FixItHint::CreateReplacement( + ExprRange, (RightSideIntValue == 0) ? "1" : SuggestedExpr); + } - bool DidWarnAboutNonPOD = false; - QualType CurrentType = ArgTy; - SmallVector Comps; - SmallVector Exprs; - for (const OffsetOfComponent &OC : Components) { - if (OC.isBrackets) { - // Offset of an array sub-field. TODO: Should we allow vector elements? - if (!CurrentType->isDependentType()) { - const ArrayType *AT = Context.getAsArrayType(CurrentType); - if(!AT) - return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) - << CurrentType); - CurrentType = AT->getElementType(); - } else - CurrentType = Context.DependentTy; + S.Diag(Loc, diag::note_xor_used_as_pow_silence) + << ("0x2 ^ " + RHSStr) << SuggestXor; + } else if (LeftSideValue == 10) { + std::string SuggestedValue = "1e" + std::to_string(RightSideIntValue); + S.Diag(Loc, diag::warn_xor_used_as_pow_base) + << ExprStr << toString(XorValue, 10, true) << SuggestedValue + << FixItHint::CreateReplacement(ExprRange, SuggestedValue); + S.Diag(Loc, diag::note_xor_used_as_pow_silence) + << ("0xA ^ " + RHSStr) << SuggestXor; + } +} - ExprResult IdxRval = DefaultLvalueConversion(static_cast(OC.U.E)); - if (IdxRval.isInvalid()) - return ExprError(); - Expr *Idx = IdxRval.get(); +QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc) { + // Ensure that either both operands are of the same vector type, or + // one operand is of a vector type and the other is of its element type. + QualType vType = CheckVectorOperands(LHS, RHS, Loc, false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ false); + if (vType.isNull()) + return InvalidOperands(Loc, LHS, RHS); + if (getLangOpts().OpenCL && + getLangOpts().getOpenCLCompatibleVersion() < 120 && + vType->hasFloatingRepresentation()) + return InvalidOperands(Loc, LHS, RHS); + // FIXME: The check for C++ here is for GCC compatibility. GCC rejects the + // usage of the logical operators && and || with vectors in C. This + // check could be notionally dropped. + if (!getLangOpts().CPlusPlus && + !(isa(vType->getAs()))) + return InvalidLogicalVectorOperands(Loc, LHS, RHS); - // The expression must be an integral expression. - // FIXME: An integral constant expression? - if (!Idx->isTypeDependent() && !Idx->isValueDependent() && - !Idx->getType()->isIntegerType()) - return ExprError( - Diag(Idx->getBeginLoc(), diag::err_typecheck_subscript_not_integer) - << Idx->getSourceRange()); + return GetSignedVectorType(LHS.get()->getType()); +} - // Record this array index. - Comps.push_back(OffsetOfNode(OC.LocStart, Exprs.size(), OC.LocEnd)); - Exprs.push_back(Idx); - continue; - } +QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - // Offset of a field. - if (CurrentType->isDependentType()) { - // We have the offset of a field, but we can't look into the dependent - // type. Just record the identifier of the field. - Comps.push_back(OffsetOfNode(OC.LocStart, OC.U.IdentInfo, OC.LocEnd)); - CurrentType = Context.DependentTy; - continue; - } + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); - // We need to have a complete type to look into. - if (RequireCompleteType(OC.LocStart, CurrentType, - diag::err_offsetof_incomplete_type)) - return ExprError(); + const MatrixType *LHSMatType = LHSType->getAs(); + const MatrixType *RHSMatType = RHSType->getAs(); + assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); - // Look for the designated field. - const RecordType *RC = CurrentType->getAs(); - if (!RC) - return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) - << CurrentType); - RecordDecl *RD = RC->getDecl(); + if (Context.hasSameType(LHSType, RHSType)) + return Context.getCommonSugaredType(LHSType, RHSType); - // C++ [lib.support.types]p5: - // The macro offsetof accepts a restricted set of type arguments in this - // International Standard. type shall be a POD structure or a POD union - // (clause 9). - // C++11 [support.types]p4: - // If type is not a standard-layout class (Clause 9), the results are - // undefined. - if (CXXRecordDecl *CRD = dyn_cast(RD)) { - bool IsSafe = LangOpts.CPlusPlus11? CRD->isStandardLayout() : CRD->isPOD(); - unsigned DiagID = - LangOpts.CPlusPlus11? diag::ext_offsetof_non_standardlayout_type - : diag::ext_offsetof_non_pod_type; + // Type conversion may change LHS/RHS. Keep copies to the original results, in + // case we have to return InvalidOperands. + ExprResult OriginalLHS = LHS; + ExprResult OriginalRHS = RHS; + if (LHSMatType && !RHSMatType) { + RHS = tryConvertExprToType(RHS.get(), LHSMatType->getElementType()); + if (!RHS.isInvalid()) + return LHSType; - if (!IsSafe && !DidWarnAboutNonPOD && !isUnevaluatedContext()) { - Diag(BuiltinLoc, DiagID) - << SourceRange(Components[0].LocStart, OC.LocEnd) << CurrentType; - DidWarnAboutNonPOD = true; - } - } + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } - // Look for the field. - LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); - LookupQualifiedName(R, RD); - FieldDecl *MemberDecl = R.getAsSingle(); - IndirectFieldDecl *IndirectMemberDecl = nullptr; - if (!MemberDecl) { - if ((IndirectMemberDecl = R.getAsSingle())) - MemberDecl = IndirectMemberDecl->getAnonField(); - } + if (!LHSMatType && RHSMatType) { + LHS = tryConvertExprToType(LHS.get(), RHSMatType->getElementType()); + if (!LHS.isInvalid()) + return RHSType; + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } - if (!MemberDecl) { - // Lookup could be ambiguous when looking up a placeholder variable - // __builtin_offsetof(S, _). - // In that case we would already have emitted a diagnostic - if (!R.isAmbiguous()) - Diag(BuiltinLoc, diag::err_no_member) - << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd); - return ExprError(); - } + return InvalidOperands(Loc, LHS, RHS); +} - // C99 7.17p3: - // (If the specified member is a bit-field, the behavior is undefined.) - // - // We diagnose this as an error. - if (MemberDecl->isBitField()) { - Diag(OC.LocEnd, diag::err_offsetof_bitfield) - << MemberDecl->getDeclName() - << SourceRange(BuiltinLoc, RParenLoc); - Diag(MemberDecl->getLocation(), diag::note_bitfield_decl); - return ExprError(); - } +QualType Sema::CheckMatrixMultiplyOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - RecordDecl *Parent = MemberDecl->getParent(); - if (IndirectMemberDecl) - Parent = cast(IndirectMemberDecl->getDeclContext()); + auto *LHSMatType = LHS.get()->getType()->getAs(); + auto *RHSMatType = RHS.get()->getType()->getAs(); + assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); - // If the member was found in a base class, introduce OffsetOfNodes for - // the base class indirections. - CXXBasePaths Paths; - if (IsDerivedFrom(OC.LocStart, CurrentType, Context.getTypeDeclType(Parent), - Paths)) { - if (Paths.getDetectedVirtual()) { - Diag(OC.LocEnd, diag::err_offsetof_field_of_virtual_base) - << MemberDecl->getDeclName() - << SourceRange(BuiltinLoc, RParenLoc); - return ExprError(); - } + if (LHSMatType && RHSMatType) { + if (LHSMatType->getNumColumns() != RHSMatType->getNumRows()) + return InvalidOperands(Loc, LHS, RHS); - CXXBasePath &Path = Paths.front(); - for (const CXXBasePathElement &B : Path) - Comps.push_back(OffsetOfNode(B.Base)); - } + if (Context.hasSameType(LHSMatType, RHSMatType)) + return Context.getCommonSugaredType( + LHS.get()->getType().getUnqualifiedType(), + RHS.get()->getType().getUnqualifiedType()); - if (IndirectMemberDecl) { - for (auto *FI : IndirectMemberDecl->chain()) { - assert(isa(FI)); - Comps.push_back(OffsetOfNode(OC.LocStart, - cast(FI), OC.LocEnd)); - } - } else - Comps.push_back(OffsetOfNode(OC.LocStart, MemberDecl, OC.LocEnd)); + QualType LHSELTy = LHSMatType->getElementType(), + RHSELTy = RHSMatType->getElementType(); + if (!Context.hasSameType(LHSELTy, RHSELTy)) + return InvalidOperands(Loc, LHS, RHS); - CurrentType = MemberDecl->getType().getNonReferenceType(); + return Context.getConstantMatrixType( + Context.getCommonSugaredType(LHSELTy, RHSELTy), + LHSMatType->getNumRows(), RHSMatType->getNumColumns()); } - - return OffsetOfExpr::Create(Context, Context.getSizeType(), BuiltinLoc, TInfo, - Comps, Exprs, RParenLoc); + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); } -ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, - SourceLocation BuiltinLoc, - SourceLocation TypeLoc, - ParsedType ParsedArgTy, - ArrayRef Components, - SourceLocation RParenLoc) { - - TypeSourceInfo *ArgTInfo; - QualType ArgTy = GetTypeFromParser(ParsedArgTy, &ArgTInfo); - if (ArgTy.isNull()) - return ExprError(); - - if (!ArgTInfo) - ArgTInfo = Context.getTrivialTypeSourceInfo(ArgTy, TypeLoc); - - return BuildBuiltinOffsetOf(BuiltinLoc, ArgTInfo, Components, RParenLoc); +static bool isLegalBoolVectorBinaryOp(BinaryOperatorKind Opc) { + switch (Opc) { + default: + return false; + case BO_And: + case BO_AndAssign: + case BO_Or: + case BO_OrAssign: + case BO_Xor: + case BO_XorAssign: + return true; + } } +inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); -ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, - Expr *CondExpr, - Expr *LHSExpr, Expr *RHSExpr, - SourceLocation RPLoc) { - assert((CondExpr && LHSExpr && RHSExpr) && "Missing type argument(s)"); - - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - QualType resType; - bool CondIsTrue = false; - if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { - resType = Context.DependentTy; - } else { - // The conditional expression is required to be a constant expression. - llvm::APSInt condEval(32); - ExprResult CondICE = VerifyIntegerConstantExpression( - CondExpr, &condEval, diag::err_typecheck_choose_expr_requires_constant); - if (CondICE.isInvalid()) - return ExprError(); - CondExpr = CondICE.get(); - CondIsTrue = condEval.getZExtValue(); + bool IsCompAssign = + Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign; - // If the condition is > zero, then the AST type is the same as the LHSExpr. - Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr; + bool LegalBoolVecOperator = isLegalBoolVectorBinaryOp(Opc); - resType = ActiveExpr->getType(); - VK = ActiveExpr->getValueKind(); - OK = ActiveExpr->getObjectKind(); + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ LegalBoolVecOperator, + /*ReportInvalid*/ true); + return InvalidOperands(Loc, LHS, RHS); } - return new (Context) ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, - resType, VK, OK, RPLoc, CondIsTrue); -} + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_BitwiseOp); + return InvalidOperands(Loc, LHS, RHS); + } -//===----------------------------------------------------------------------===// -// Clang Extensions. -//===----------------------------------------------------------------------===// + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_BitwiseOp); + return InvalidOperands(Loc, LHS, RHS); + } -void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { - BlockDecl *Block = BlockDecl::Create(Context, CurContext, CaretLoc); + if (Opc == BO_And) + diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); - if (LangOpts.CPlusPlus) { - MangleNumberingContext *MCtx; - Decl *ManglingContextDecl; - std::tie(MCtx, ManglingContextDecl) = - getCurrentMangleNumberContext(Block->getDeclContext()); - if (MCtx) { - unsigned ManglingNumber = MCtx->getManglingNumber(Block); - Block->setBlockMangling(ManglingNumber, ManglingContextDecl); - } - } + if (LHS.get()->getType()->hasFloatingRepresentation() || + RHS.get()->getType()->hasFloatingRepresentation()) + return InvalidOperands(Loc, LHS, RHS); - PushBlockScope(CurScope, Block); - CurContext->addDecl(Block); - if (CurScope) - PushDeclContext(CurScope, Block); - else - CurContext = Block; + ExprResult LHSResult = LHS, RHSResult = RHS; + QualType compType = UsualArithmeticConversions( + LHSResult, RHSResult, Loc, IsCompAssign ? ACK_CompAssign : ACK_BitwiseOp); + if (LHSResult.isInvalid() || RHSResult.isInvalid()) + return QualType(); + LHS = LHSResult.get(); + RHS = RHSResult.get(); - getCurBlock()->HasImplicitReturnType = true; + if (Opc == BO_Xor) + diagnoseXorMisusedAsPow(*this, LHS, RHS, Loc); - // Enter a new evaluation context to insulate the block from any - // cleanups from the enclosing full-expression. - PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated); + if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) + return compType; + return InvalidOperands(Loc, LHS, RHS); } -void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, - Scope *CurScope) { - assert(ParamInfo.getIdentifier() == nullptr && - "block-id should have no identifier!"); - assert(ParamInfo.getContext() == DeclaratorContext::BlockLiteral); - BlockScopeInfo *CurBlock = getCurBlock(); - - TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo); - QualType T = Sig->getType(); +// C99 6.5.[13,14] +inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + // Check vector operands differently. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorLogicalOperands(LHS, RHS, Loc); - // FIXME: We should allow unexpanded parameter packs here, but that would, - // in turn, make the block expression contain unexpanded parameter packs. - if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) { - // Drop the parameters. - FunctionProtoType::ExtProtoInfo EPI; - EPI.HasTrailingReturn = false; - EPI.TypeQuals.addConst(); - T = Context.getFunctionType(Context.DependentTy, std::nullopt, EPI); - Sig = Context.getTrivialTypeSourceInfo(T); + bool EnumConstantInBoolContext = false; + for (const ExprResult &HS : {LHS, RHS}) { + if (const auto *DREHS = dyn_cast(HS.get())) { + const auto *ECDHS = dyn_cast(DREHS->getDecl()); + if (ECDHS && ECDHS->getInitVal() != 0 && ECDHS->getInitVal() != 1) + EnumConstantInBoolContext = true; + } } - // GetTypeForDeclarator always produces a function type for a block - // literal signature. Furthermore, it is always a FunctionProtoType - // unless the function was written with a typedef. - assert(T->isFunctionType() && - "GetTypeForDeclarator made a non-function block signature"); - - // Look for an explicit signature in that function type. - FunctionProtoTypeLoc ExplicitSignature; + if (EnumConstantInBoolContext) + Diag(Loc, diag::warn_enum_constant_in_bool_context); - if ((ExplicitSignature = Sig->getTypeLoc() - .getAsAdjusted())) { + // WebAssembly tables can't be used with logical operators. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + const auto *LHSATy = dyn_cast(LHSTy); + const auto *RHSATy = dyn_cast(RHSTy); + if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || + (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { + return InvalidOperands(Loc, LHS, RHS); + } - // Check whether that explicit signature was synthesized by - // GetTypeForDeclarator. If so, don't save that as part of the - // written signature. - if (ExplicitSignature.getLocalRangeBegin() == - ExplicitSignature.getLocalRangeEnd()) { - // This would be much cheaper if we stored TypeLocs instead of - // TypeSourceInfos. - TypeLoc Result = ExplicitSignature.getReturnLoc(); - unsigned Size = Result.getFullDataSize(); - Sig = Context.CreateTypeSourceInfo(Result.getType(), Size); - Sig->getTypeLoc().initializeFullCopy(Result, Size); - - ExplicitSignature = FunctionProtoTypeLoc(); + // Diagnose cases where the user write a logical and/or but probably meant a + // bitwise one. We do this when the LHS is a non-bool integer and the RHS + // is a constant. + if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() && + !LHS.get()->getType()->isBooleanType() && + RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() && + // Don't warn in macros or template instantiations. + !Loc.isMacroID() && !inTemplateInstantiation()) { + // If the RHS can be constant folded, and if it constant folds to something + // that isn't 0 or 1 (which indicate a potential logical operation that + // happened to fold to true/false) then warn. + // Parens on the RHS are ignored. + Expr::EvalResult EVResult; + if (RHS.get()->EvaluateAsInt(EVResult, Context)) { + llvm::APSInt Result = EVResult.Val.getInt(); + if ((getLangOpts().CPlusPlus && !RHS.get()->getType()->isBooleanType() && + !RHS.get()->getExprLoc().isMacroID()) || + (Result != 0 && Result != 1)) { + Diag(Loc, diag::warn_logical_instead_of_bitwise) + << RHS.get()->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||"); + // Suggest replacing the logical operator with the bitwise version + Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator) + << (Opc == BO_LAnd ? "&" : "|") + << FixItHint::CreateReplacement( + SourceRange(Loc, getLocForEndOfToken(Loc)), + Opc == BO_LAnd ? "&" : "|"); + if (Opc == BO_LAnd) + // Suggest replacing "Foo() && kNonZero" with "Foo()" + Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant) + << FixItHint::CreateRemoval( + SourceRange(getLocForEndOfToken(LHS.get()->getEndLoc()), + RHS.get()->getEndLoc())); + } } } - CurBlock->TheDecl->setSignatureAsWritten(Sig); - CurBlock->FunctionType = T; - - const auto *Fn = T->castAs(); - QualType RetTy = Fn->getReturnType(); - bool isVariadic = - (isa(Fn) && cast(Fn)->isVariadic()); - - CurBlock->TheDecl->setIsVariadic(isVariadic); + if (!Context.getLangOpts().CPlusPlus) { + // OpenCL v1.1 s6.3.g: The logical operators and (&&), or (||) do + // not operate on the built-in scalar and vector float types. + if (Context.getLangOpts().OpenCL && + Context.getLangOpts().OpenCLVersion < 120) { + if (LHS.get()->getType()->isFloatingType() || + RHS.get()->getType()->isFloatingType()) + return InvalidOperands(Loc, LHS, RHS); + } - // Context.DependentTy is used as a placeholder for a missing block - // return type. TODO: what should we do with declarators like: - // ^ * { ... } - // If the answer is "apply template argument deduction".... - if (RetTy != Context.DependentTy) { - CurBlock->ReturnType = RetTy; - CurBlock->TheDecl->setBlockMissingReturnType(false); - CurBlock->HasImplicitReturnType = false; - } + LHS = UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); - // Push block parameters from the declarator if we had them. - SmallVector Params; - if (ExplicitSignature) { - for (unsigned I = 0, E = ExplicitSignature.getNumParams(); I != E; ++I) { - ParmVarDecl *Param = ExplicitSignature.getParam(I); - if (Param->getIdentifier() == nullptr && !Param->isImplicit() && - !Param->isInvalidDecl() && !getLangOpts().CPlusPlus) { - // Diagnose this as an extension in C17 and earlier. - if (!getLangOpts().C23) - Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23); - } - Params.push_back(Param); - } + RHS = UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - // Fake up parameter variables if we have a typedef, like - // ^ fntype { ... } - } else if (const FunctionProtoType *Fn = T->getAs()) { - for (const auto &I : Fn->param_types()) { - ParmVarDecl *Param = BuildParmVarDeclForTypedef( - CurBlock->TheDecl, ParamInfo.getBeginLoc(), I); - Params.push_back(Param); - } - } + if (!LHS.get()->getType()->isScalarType() || + !RHS.get()->getType()->isScalarType()) + return InvalidOperands(Loc, LHS, RHS); - // Set the parameters on the block decl. - if (!Params.empty()) { - CurBlock->TheDecl->setParams(Params); - CheckParmsForFunctionDef(CurBlock->TheDecl->parameters(), - /*CheckParameterNames=*/false); + return Context.IntTy; } - // Finally we can process decl attributes. - ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo); - - // Put the parameter variables in scope. - for (auto *AI : CurBlock->TheDecl->parameters()) { - AI->setOwningFunction(CurBlock->TheDecl); + // The following is safe because we only use this method for + // non-overloadable operands. - // If this has an identifier, add it to the scope stack. - if (AI->getIdentifier()) { - CheckShadow(CurBlock->TheScope, AI); + // C++ [expr.log.and]p1 + // C++ [expr.log.or]p1 + // The operands are both contextually converted to type bool. + ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get()); + if (LHSRes.isInvalid()) + return InvalidOperands(Loc, LHS, RHS); + LHS = LHSRes; - PushOnScopeChains(AI, CurBlock->TheScope); - } + ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); + if (RHSRes.isInvalid()) + return InvalidOperands(Loc, LHS, RHS); + RHS = RHSRes; - if (AI->isInvalidDecl()) - CurBlock->TheDecl->setInvalidDecl(); - } + // C++ [expr.log.and]p2 + // C++ [expr.log.or]p2 + // The result is a bool. + return Context.BoolTy; } -void Sema::ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) { - // Leave the expression-evaluation context. - DiscardCleanupsInEvaluationContext(); - PopExpressionEvaluationContext(); - - // Pop off CurBlock, handle nested blocks. - PopDeclContext(); - PopFunctionScopeInfo(); +static bool IsReadonlyMessage(Expr *E, Sema &S) { + const MemberExpr *ME = dyn_cast(E); + if (!ME) return false; + if (!isa(ME->getMemberDecl())) return false; + ObjCMessageExpr *Base = dyn_cast( + ME->getBase()->IgnoreImplicit()->IgnoreParenImpCasts()); + if (!Base) return false; + return Base->getMethodDecl() != nullptr; } -ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, - Stmt *Body, Scope *CurScope) { - // If blocks are disabled, emit an error. - if (!LangOpts.Blocks) - Diag(CaretLoc, diag::err_blocks_disable) << LangOpts.OpenCL; +/// Is the given expression (which must be 'const') a reference to a +/// variable which was originally non-const, but which has become +/// 'const' due to being captured within a block? +enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda }; +static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { + assert(E->isLValue() && E->getType().isConstQualified()); + E = E->IgnoreParens(); - // Leave the expression-evaluation context. - if (hasAnyUnrecoverableErrorsInThisFunction()) - DiscardCleanupsInEvaluationContext(); - assert(!Cleanup.exprNeedsCleanups() && - "cleanups within block not correctly bound!"); - PopExpressionEvaluationContext(); + // Must be a reference to a declaration from an enclosing scope. + DeclRefExpr *DRE = dyn_cast(E); + if (!DRE) return NCCK_None; + if (!DRE->refersToEnclosingVariableOrCapture()) return NCCK_None; - BlockScopeInfo *BSI = cast(FunctionScopes.back()); - BlockDecl *BD = BSI->TheDecl; + // The declaration must be a variable which is not declared 'const'. + VarDecl *var = dyn_cast(DRE->getDecl()); + if (!var) return NCCK_None; + if (var->getType().isConstQualified()) return NCCK_None; + assert(var->hasLocalStorage() && "capture added 'const' to non-local?"); - maybeAddDeclWithEffects(BD); + // Decide whether the first capture was for a block or a lambda. + DeclContext *DC = S.CurContext, *Prev = nullptr; + // Decide whether the first capture was for a block or a lambda. + while (DC) { + // For init-capture, it is possible that the variable belongs to the + // template pattern of the current context. + if (auto *FD = dyn_cast(DC)) + if (var->isInitCapture() && + FD->getTemplateInstantiationPattern() == var->getDeclContext()) + break; + if (DC == var->getDeclContext()) + break; + Prev = DC; + DC = DC->getParent(); + } + // Unless we have an init-capture, we've gone one step too far. + if (!var->isInitCapture()) + DC = Prev; + return (isa(DC) ? NCCK_Block : NCCK_Lambda); +} - if (BSI->HasImplicitReturnType) - deduceClosureReturnType(*BSI); +static bool IsTypeModifiable(QualType Ty, bool IsDereference) { + Ty = Ty.getNonReferenceType(); + if (IsDereference && Ty->isPointerType()) + Ty = Ty->getPointeeType(); + return !Ty.isConstQualified(); +} - QualType RetTy = Context.VoidTy; - if (!BSI->ReturnType.isNull()) - RetTy = BSI->ReturnType; +// Update err_typecheck_assign_const and note_typecheck_assign_const +// when this enum is changed. +enum { + ConstFunction, + ConstVariable, + ConstMember, + ConstMethod, + NestedConstMember, + ConstUnknown, // Keep as last element +}; - bool NoReturn = BD->hasAttr(); - QualType BlockTy; +/// Emit the "read-only variable not assignable" error and print notes to give +/// more information about why the variable is not assignable, such as pointing +/// to the declaration of a const variable, showing that a method is const, or +/// that the function is returning a const reference. +static void DiagnoseConstAssignment(Sema &S, const Expr *E, + SourceLocation Loc) { + SourceRange ExprRange = E->getSourceRange(); - // If the user wrote a function type in some form, try to use that. - if (!BSI->FunctionType.isNull()) { - const FunctionType *FTy = BSI->FunctionType->castAs(); + // Only emit one error on the first const found. All other consts will emit + // a note to the error. + bool DiagnosticEmitted = false; - FunctionType::ExtInfo Ext = FTy->getExtInfo(); - if (NoReturn && !Ext.getNoReturn()) Ext = Ext.withNoReturn(true); + // Track if the current expression is the result of a dereference, and if the + // next checked expression is the result of a dereference. + bool IsDereference = false; + bool NextIsDereference = false; - // Turn protoless block types into nullary block types. - if (isa(FTy)) { - FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = Ext; - BlockTy = Context.getFunctionType(RetTy, std::nullopt, EPI); + // Loop to process MemberExpr chains. + while (true) { + IsDereference = NextIsDereference; - // Otherwise, if we don't need to change anything about the function type, - // preserve its sugar structure. - } else if (FTy->getReturnType() == RetTy && - (!NoReturn || FTy->getNoReturnAttr())) { - BlockTy = BSI->FunctionType; + E = E->IgnoreImplicit()->IgnoreParenImpCasts(); + if (const MemberExpr *ME = dyn_cast(E)) { + NextIsDereference = ME->isArrow(); + const ValueDecl *VD = ME->getMemberDecl(); + if (const FieldDecl *Field = dyn_cast(VD)) { + // Mutable fields can be modified even if the class is const. + if (Field->isMutable()) { + assert(DiagnosticEmitted && "Expected diagnostic not emitted."); + break; + } - // Otherwise, make the minimal modifications to the function type. - } else { - const FunctionProtoType *FPT = cast(FTy); - FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); - EPI.TypeQuals = Qualifiers(); - EPI.ExtInfo = Ext; - BlockTy = Context.getFunctionType(RetTy, FPT->getParamTypes(), EPI); + if (!IsTypeModifiable(Field->getType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstMember << false /*static*/ << Field + << Field->getType(); + DiagnosticEmitted = true; + } + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstMember << false /*static*/ << Field << Field->getType() + << Field->getSourceRange(); + } + E = ME->getBase(); + continue; + } else if (const VarDecl *VDecl = dyn_cast(VD)) { + if (VDecl->getType().isConstQualified()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstMember << true /*static*/ << VDecl + << VDecl->getType(); + DiagnosticEmitted = true; + } + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstMember << true /*static*/ << VDecl << VDecl->getType() + << VDecl->getSourceRange(); + } + // Static fields do not inherit constness from parents. + break; + } + break; // End MemberExpr + } else if (const ArraySubscriptExpr *ASE = + dyn_cast(E)) { + E = ASE->getBase()->IgnoreParenImpCasts(); + continue; + } else if (const ExtVectorElementExpr *EVE = + dyn_cast(E)) { + E = EVE->getBase()->IgnoreParenImpCasts(); + continue; } - - // If we don't have a function type, just build one from nothing. - } else { - FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); - BlockTy = Context.getFunctionType(RetTy, std::nullopt, EPI); + break; } - DiagnoseUnusedParameters(BD->parameters()); - BlockTy = Context.getBlockPointerType(BlockTy); - - // If needed, diagnose invalid gotos and switches in the block. - if (getCurFunction()->NeedsScopeChecking() && - !PP.isCodeCompletionEnabled()) - DiagnoseInvalidJumps(cast(Body)); - - BD->setBody(cast(Body)); - - if (Body && getCurFunction()->HasPotentialAvailabilityViolations) - DiagnoseUnguardedAvailabilityViolations(BD); - - // Try to apply the named return value optimization. We have to check again - // if we can do this, though, because blocks keep return statements around - // to deduce an implicit return type. - if (getLangOpts().CPlusPlus && RetTy->isRecordType() && - !BD->isDependentContext()) - computeNRVO(Body, BSI); - - if (RetTy.hasNonTrivialToPrimitiveDestructCUnion() || - RetTy.hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn, - NTCUK_Destruct|NTCUK_Copy); - - PopDeclContext(); - - // Set the captured variables on the block. - SmallVector Captures; - for (Capture &Cap : BSI->Captures) { - if (Cap.isInvalid() || Cap.isThisCapture()) - continue; - // Cap.getVariable() is always a VarDecl because - // blocks cannot capture structured bindings or other ValueDecl kinds. - auto *Var = cast(Cap.getVariable()); - Expr *CopyExpr = nullptr; - if (getLangOpts().CPlusPlus && Cap.isCopyCapture()) { - if (const RecordType *Record = - Cap.getCaptureType()->getAs()) { - // The capture logic needs the destructor, so make sure we mark it. - // Usually this is unnecessary because most local variables have - // their destructors marked at declaration time, but parameters are - // an exception because it's technically only the call site that - // actually requires the destructor. - if (isa(Var)) - FinalizeVarWithDestructor(Var, Record); - - // Enter a separate potentially-evaluated context while building block - // initializers to isolate their cleanups from those of the block - // itself. - // FIXME: Is this appropriate even when the block itself occurs in an - // unevaluated operand? - EnterExpressionEvaluationContext EvalContext( - *this, ExpressionEvaluationContext::PotentiallyEvaluated); - - SourceLocation Loc = Cap.getLocation(); - - ExprResult Result = BuildDeclarationNameExpr( - CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var); - - // According to the blocks spec, the capture of a variable from - // the stack requires a const copy constructor. This is not true - // of the copy/move done to move a __block variable to the heap. - if (!Result.isInvalid() && - !Result.get()->getType().isConstQualified()) { - Result = ImpCastExprToType(Result.get(), - Result.get()->getType().withConst(), - CK_NoOp, VK_LValue); - } - - if (!Result.isInvalid()) { - Result = PerformCopyInitialization( - InitializedEntity::InitializeBlock(Var->getLocation(), - Cap.getCaptureType()), - Loc, Result.get()); + if (const CallExpr *CE = dyn_cast(E)) { + // Function calls + const FunctionDecl *FD = CE->getDirectCallee(); + if (FD && !IsTypeModifiable(FD->getReturnType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange + << ConstFunction << FD; + DiagnosticEmitted = true; + } + S.Diag(FD->getReturnTypeSourceRange().getBegin(), + diag::note_typecheck_assign_const) + << ConstFunction << FD << FD->getReturnType() + << FD->getReturnTypeSourceRange(); + } + } else if (const DeclRefExpr *DRE = dyn_cast(E)) { + // Point to variable declaration. + if (const ValueDecl *VD = DRE->getDecl()) { + if (!IsTypeModifiable(VD->getType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstVariable << VD << VD->getType(); + DiagnosticEmitted = true; } - - // Build a full-expression copy expression if initialization - // succeeded and used a non-trivial constructor. Recover from - // errors by pretending that the copy isn't necessary. - if (!Result.isInvalid() && - !cast(Result.get())->getConstructor() - ->isTrivial()) { - Result = MaybeCreateExprWithCleanups(Result); - CopyExpr = Result.get(); + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstVariable << VD << VD->getType() << VD->getSourceRange(); + } + } + } else if (isa(E)) { + if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { + if (const CXXMethodDecl *MD = dyn_cast(DC)) { + if (MD->isConst()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange + << ConstMethod << MD; + DiagnosticEmitted = true; + } + S.Diag(MD->getLocation(), diag::note_typecheck_assign_const) + << ConstMethod << MD << MD->getSourceRange(); } } } - - BlockDecl::Capture NewCap(Var, Cap.isBlockCapture(), Cap.isNested(), - CopyExpr); - Captures.push_back(NewCap); } - BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); - // Pop the block scope now but keep it alive to the end of this function. - AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); - PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); + if (DiagnosticEmitted) + return; - BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy); + // Can't determine a more specific message, so display the generic error. + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown; +} - // If the block isn't obviously global, i.e. it captures anything at - // all, then we need to do a few things in the surrounding context: - if (Result->getBlockDecl()->hasCaptures()) { - // First, this expression has a new cleanup object. - ExprCleanupObjects.push_back(Result->getBlockDecl()); - Cleanup.setExprNeedsCleanups(true); +enum OriginalExprKind { + OEK_Variable, + OEK_Member, + OEK_LValue +}; - // It also gets a branch-protected scope if any of the captured - // variables needs destruction. - for (const auto &CI : Result->getBlockDecl()->captures()) { - const VarDecl *var = CI.getVariable(); - if (var->getType().isDestructedType() != QualType::DK_none) { - setFunctionHasBranchProtectedScope(); - break; +static void DiagnoseRecursiveConstFields(Sema &S, const ValueDecl *VD, + const RecordType *Ty, + SourceLocation Loc, SourceRange Range, + OriginalExprKind OEK, + bool &DiagnosticEmitted) { + std::vector RecordTypeList; + RecordTypeList.push_back(Ty); + unsigned NextToCheckIndex = 0; + // We walk the record hierarchy breadth-first to ensure that we print + // diagnostics in field nesting order. + while (RecordTypeList.size() > NextToCheckIndex) { + bool IsNested = NextToCheckIndex > 0; + for (const FieldDecl *Field : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + // First, check every field for constness. + QualType FieldTy = Field->getType(); + if (FieldTy.isConstQualified()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << Range << NestedConstMember << OEK << VD + << IsNested << Field; + DiagnosticEmitted = true; + } + S.Diag(Field->getLocation(), diag::note_typecheck_assign_const) + << NestedConstMember << IsNested << Field + << FieldTy << Field->getSourceRange(); + } + + // Then we append it to the list to check next in order. + FieldTy = FieldTy.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + RecordTypeList.push_back(FieldRecTy); } } + ++NextToCheckIndex; } +} - if (getCurFunction()) - getCurFunction()->addBlock(BD); - - if (BD->isInvalidDecl()) - return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(), - {Result}, Result->getType()); - return Result; -} +/// Emit an error for the case where a record we are trying to assign to has a +/// const-qualified field somewhere in its hierarchy. +static void DiagnoseRecursiveConstFields(Sema &S, const Expr *E, + SourceLocation Loc) { + QualType Ty = E->getType(); + assert(Ty->isRecordType() && "lvalue was not record?"); + SourceRange Range = E->getSourceRange(); + const RecordType *RTy = Ty.getCanonicalType()->getAs(); + bool DiagEmitted = false; -ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc, Expr *E, ParsedType Ty, - SourceLocation RPLoc) { - TypeSourceInfo *TInfo; - GetTypeFromParser(Ty, &TInfo); - return BuildVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); + if (const MemberExpr *ME = dyn_cast(E)) + DiagnoseRecursiveConstFields(S, ME->getMemberDecl(), RTy, Loc, + Range, OEK_Member, DiagEmitted); + else if (const DeclRefExpr *DRE = dyn_cast(E)) + DiagnoseRecursiveConstFields(S, DRE->getDecl(), RTy, Loc, + Range, OEK_Variable, DiagEmitted); + else + DiagnoseRecursiveConstFields(S, nullptr, RTy, Loc, + Range, OEK_LValue, DiagEmitted); + if (!DiagEmitted) + DiagnoseConstAssignment(S, E, Loc); } -ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, - Expr *E, TypeSourceInfo *TInfo, - SourceLocation RPLoc) { - Expr *OrigExpr = E; - bool IsMS = false; - - // CUDA device code does not support varargs. - if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) { - if (const FunctionDecl *F = dyn_cast(CurContext)) { - CUDAFunctionTarget T = CUDA().IdentifyTarget(F); - if (T == CUDAFunctionTarget::Global || T == CUDAFunctionTarget::Device || - T == CUDAFunctionTarget::HostDevice) - return ExprError(Diag(E->getBeginLoc(), diag::err_va_arg_in_device)); - } - } +/// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, +/// emit an error and return true. If so, return false. +static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { + assert(!E->hasPlaceholderType(BuiltinType::PseudoObject)); - // NVPTX does not support va_arg expression. - if (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && - Context.getTargetInfo().getTriple().isNVPTX()) - targetDiag(E->getBeginLoc(), diag::err_va_arg_in_device); + S.CheckShadowingDeclModification(E, Loc); - // It might be a __builtin_ms_va_list. (But don't ever mark a va_arg() - // as Microsoft ABI on an actual Microsoft platform, where - // __builtin_ms_va_list and __builtin_va_list are the same.) - if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinMSVaList() && - Context.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList) { - QualType MSVaListType = Context.getBuiltinMSVaListType(); - if (Context.hasSameType(MSVaListType, E->getType())) { - if (CheckForModifiableLvalue(E, BuiltinLoc, *this)) - return ExprError(); - IsMS = true; - } - } + SourceLocation OrigLoc = Loc; + Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, + &Loc); + if (IsLV == Expr::MLV_ClassTemporary && IsReadonlyMessage(E, S)) + IsLV = Expr::MLV_InvalidMessageExpression; + if (IsLV == Expr::MLV_Valid) + return false; - // Get the va_list type - QualType VaListType = Context.getBuiltinVaListType(); - if (!IsMS) { - if (VaListType->isArrayType()) { - // Deal with implicit array decay; for example, on x86-64, - // va_list is an array, but it's supposed to decay to - // a pointer for va_arg. - VaListType = Context.getArrayDecayedType(VaListType); - // Make sure the input expression also decays appropriately. - ExprResult Result = UsualUnaryConversions(E); - if (Result.isInvalid()) - return ExprError(); - E = Result.get(); - } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) { - // If va_list is a record type and we are compiling in C++ mode, - // check the argument using reference binding. - InitializedEntity Entity = InitializedEntity::InitializeParameter( - Context, Context.getLValueReferenceType(VaListType), false); - ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E); - if (Init.isInvalid()) - return ExprError(); - E = Init.getAs(); - } else { - // Otherwise, the va_list argument must be an l-value because - // it is modified by va_arg. - if (!E->isTypeDependent() && - CheckForModifiableLvalue(E, BuiltinLoc, *this)) - return ExprError(); + unsigned DiagID = 0; + bool NeedType = false; + switch (IsLV) { // C99 6.5.16p2 + case Expr::MLV_ConstQualified: + // Use a specialized diagnostic when we're assigning to an object + // from an enclosing function or block. + if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) { + if (NCCK == NCCK_Block) + DiagID = diag::err_block_decl_ref_not_modifiable_lvalue; + else + DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue; + break; } - } - - if (!IsMS && !E->isTypeDependent() && - !Context.hasSameType(VaListType, E->getType())) - return ExprError( - Diag(E->getBeginLoc(), - diag::err_first_argument_to_va_arg_not_of_type_va_list) - << OrigExpr->getType() << E->getSourceRange()); - if (!TInfo->getType()->isDependentType()) { - if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), - diag::err_second_parameter_to_va_arg_incomplete, - TInfo->getTypeLoc())) - return ExprError(); + // In ARC, use some specialized diagnostics for occasions where we + // infer 'const'. These are always pseudo-strong variables. + if (S.getLangOpts().ObjCAutoRefCount) { + DeclRefExpr *declRef = dyn_cast(E->IgnoreParenCasts()); + if (declRef && isa(declRef->getDecl())) { + VarDecl *var = cast(declRef->getDecl()); - if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType(), - diag::err_second_parameter_to_va_arg_abstract, - TInfo->getTypeLoc())) - return ExprError(); + // Use the normal diagnostic if it's pseudo-__strong but the + // user actually wrote 'const'. + if (var->isARCPseudoStrong() && + (!var->getTypeSourceInfo() || + !var->getTypeSourceInfo()->getType().isConstQualified())) { + // There are three pseudo-strong cases: + // - self + ObjCMethodDecl *method = S.getCurMethodDecl(); + if (method && var == method->getSelfDecl()) { + DiagID = method->isClassMethod() + ? diag::err_typecheck_arc_assign_self_class_method + : diag::err_typecheck_arc_assign_self; - if (!TInfo->getType().isPODType(Context)) { - Diag(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType()->isObjCLifetimeType() - ? diag::warn_second_parameter_to_va_arg_ownership_qualified - : diag::warn_second_parameter_to_va_arg_not_pod) - << TInfo->getType() - << TInfo->getTypeLoc().getSourceRange(); - } + // - Objective-C externally_retained attribute. + } else if (var->hasAttr() || + isa(var)) { + DiagID = diag::err_typecheck_arc_assign_externally_retained; - // Check for va_arg where arguments of the given type will be promoted - // (i.e. this va_arg is guaranteed to have undefined behavior). - QualType PromoteType; - if (Context.isPromotableIntegerType(TInfo->getType())) { - PromoteType = Context.getPromotedIntegerType(TInfo->getType()); - // [cstdarg.syn]p1 defers the C++ behavior to what the C standard says, - // and C23 7.16.1.1p2 says, in part: - // If type is not compatible with the type of the actual next argument - // (as promoted according to the default argument promotions), the - // behavior is undefined, except for the following cases: - // - both types are pointers to qualified or unqualified versions of - // compatible types; - // - one type is compatible with a signed integer type, the other - // type is compatible with the corresponding unsigned integer type, - // and the value is representable in both types; - // - one type is pointer to qualified or unqualified void and the - // other is a pointer to a qualified or unqualified character type; - // - or, the type of the next argument is nullptr_t and type is a - // pointer type that has the same representation and alignment - // requirements as a pointer to a character type. - // Given that type compatibility is the primary requirement (ignoring - // qualifications), you would think we could call typesAreCompatible() - // directly to test this. However, in C++, that checks for *same type*, - // which causes false positives when passing an enumeration type to - // va_arg. Instead, get the underlying type of the enumeration and pass - // that. - QualType UnderlyingType = TInfo->getType(); - if (const auto *ET = UnderlyingType->getAs()) - UnderlyingType = ET->getDecl()->getIntegerType(); - if (Context.typesAreCompatible(PromoteType, UnderlyingType, - /*CompareUnqualified*/ true)) - PromoteType = QualType(); + // - fast enumeration variables + } else { + DiagID = diag::err_typecheck_arr_assign_enumeration; + } - // If the types are still not compatible, we need to test whether the - // promoted type and the underlying type are the same except for - // signedness. Ask the AST for the correctly corresponding type and see - // if that's compatible. - if (!PromoteType.isNull() && !UnderlyingType->isBooleanType() && - PromoteType->isUnsignedIntegerType() != - UnderlyingType->isUnsignedIntegerType()) { - UnderlyingType = - UnderlyingType->isUnsignedIntegerType() - ? Context.getCorrespondingSignedType(UnderlyingType) - : Context.getCorrespondingUnsignedType(UnderlyingType); - if (Context.typesAreCompatible(PromoteType, UnderlyingType, - /*CompareUnqualified*/ true)) - PromoteType = QualType(); + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; + // We need to preserve the AST regardless, so migration tool + // can do its job. + return false; + } } } - if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) - PromoteType = Context.DoubleTy; - if (!PromoteType.isNull()) - DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, - PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) - << TInfo->getType() - << PromoteType - << TInfo->getTypeLoc().getSourceRange()); - } - - QualType T = TInfo->getType().getNonLValueExprType(Context); - return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS); -} - -ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { - // The type of __null will be int or long, depending on the size of - // pointers on the target. - QualType Ty; - unsigned pw = Context.getTargetInfo().getPointerWidth(LangAS::Default); - if (pw == Context.getTargetInfo().getIntWidth()) - Ty = Context.IntTy; - else if (pw == Context.getTargetInfo().getLongWidth()) - Ty = Context.LongTy; - else if (pw == Context.getTargetInfo().getLongLongWidth()) - Ty = Context.LongLongTy; - else { - llvm_unreachable("I don't know size of pointer!"); - } - return new (Context) GNUNullExpr(Ty, TokenLoc); -} + // If none of the special cases above are triggered, then this is a + // simple const assignment. + if (DiagID == 0) { + DiagnoseConstAssignment(S, E, Loc); + return true; + } -static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { - CXXRecordDecl *ImplDecl = nullptr; - - // Fetch the std::source_location::__impl decl. - if (NamespaceDecl *Std = S.getStdNamespace()) { - LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), - Loc, Sema::LookupOrdinaryName); - if (S.LookupQualifiedName(ResultSL, Std)) { - if (auto *SLDecl = ResultSL.getAsSingle()) { - LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), - Loc, Sema::LookupOrdinaryName); - if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && - S.LookupQualifiedName(ResultImpl, SLDecl)) { - ImplDecl = ResultImpl.getAsSingle(); - } - } - } + break; + case Expr::MLV_ConstAddrSpace: + DiagnoseConstAssignment(S, E, Loc); + return true; + case Expr::MLV_ConstQualifiedField: + DiagnoseRecursiveConstFields(S, E, Loc); + return true; + case Expr::MLV_ArrayType: + case Expr::MLV_ArrayTemporary: + DiagID = diag::err_typecheck_array_not_modifiable_lvalue; + NeedType = true; + break; + case Expr::MLV_NotObjectType: + DiagID = diag::err_typecheck_non_object_not_modifiable_lvalue; + NeedType = true; + break; + case Expr::MLV_LValueCast: + DiagID = diag::err_typecheck_lvalue_casts_not_supported; + break; + case Expr::MLV_Valid: + llvm_unreachable("did not take early return for MLV_Valid"); + case Expr::MLV_InvalidExpression: + case Expr::MLV_MemberFunction: + case Expr::MLV_ClassTemporary: + DiagID = diag::err_typecheck_expression_not_modifiable_lvalue; + break; + case Expr::MLV_IncompleteType: + case Expr::MLV_IncompleteVoidType: + return S.RequireCompleteType(Loc, E->getType(), + diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); + case Expr::MLV_DuplicateVectorComponents: + DiagID = diag::err_typecheck_duplicate_vector_components_not_mlvalue; + break; + case Expr::MLV_NoSetterProperty: + llvm_unreachable("readonly properties should be processed differently"); + case Expr::MLV_InvalidMessageExpression: + DiagID = diag::err_readonly_message_assignment; + break; + case Expr::MLV_SubObjCPropertySetting: + DiagID = diag::err_no_subobject_property_setting; + break; } - if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { - S.Diag(Loc, diag::err_std_source_location_impl_not_found); - return nullptr; - } + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + if (NeedType) + S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign; + else + S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; + return true; +} - // Verify that __impl is a trivial struct type, with no base classes, and with - // only the four expected fields. - if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || - ImplDecl->getNumBases() != 0) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); - return nullptr; - } +static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr, + SourceLocation Loc, + Sema &Sema) { + if (Sema.inTemplateInstantiation()) + return; + if (Sema.isUnevaluatedContext()) + return; + if (Loc.isInvalid() || Loc.isMacroID()) + return; + if (LHSExpr->getExprLoc().isMacroID() || RHSExpr->getExprLoc().isMacroID()) + return; - unsigned Count = 0; - for (FieldDecl *F : ImplDecl->fields()) { - StringRef Name = F->getName(); + // C / C++ fields + MemberExpr *ML = dyn_cast(LHSExpr); + MemberExpr *MR = dyn_cast(RHSExpr); + if (ML && MR) { + if (!(isa(ML->getBase()) && isa(MR->getBase()))) + return; + const ValueDecl *LHSDecl = + cast(ML->getMemberDecl()->getCanonicalDecl()); + const ValueDecl *RHSDecl = + cast(MR->getMemberDecl()->getCanonicalDecl()); + if (LHSDecl != RHSDecl) + return; + if (LHSDecl->getType().isVolatileQualified()) + return; + if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) + if (RefTy->getPointeeType().isVolatileQualified()) + return; - if (Name == "_M_file_name") { - if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) - break; - Count++; - } else if (Name == "_M_function_name") { - if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) - break; - Count++; - } else if (Name == "_M_line") { - if (!F->getType()->isIntegerType()) - break; - Count++; - } else if (Name == "_M_column") { - if (!F->getType()->isIntegerType()) - break; - Count++; - } else { - Count = 100; // invalid - break; - } - } - if (Count != 4) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); - return nullptr; + Sema.Diag(Loc, diag::warn_identity_field_assign) << 0; } - return ImplDecl; -} - -ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, - SourceLocation BuiltinLoc, - SourceLocation RPLoc) { - QualType ResultTy; - switch (Kind) { - case SourceLocIdentKind::File: - case SourceLocIdentKind::FileName: - case SourceLocIdentKind::Function: - case SourceLocIdentKind::FuncSig: { - QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); - ResultTy = - Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); - break; - } - case SourceLocIdentKind::Line: - case SourceLocIdentKind::Column: - ResultTy = Context.UnsignedIntTy; - break; - case SourceLocIdentKind::SourceLocStruct: - if (!StdSourceLocationImplDecl) { - StdSourceLocationImplDecl = - LookupStdSourceLocationImpl(*this, BuiltinLoc); - if (!StdSourceLocationImplDecl) - return ExprError(); - } - ResultTy = Context.getPointerType( - Context.getRecordType(StdSourceLocationImplDecl).withConst()); - break; + // Objective-C instance variables + ObjCIvarRefExpr *OL = dyn_cast(LHSExpr); + ObjCIvarRefExpr *OR = dyn_cast(RHSExpr); + if (OL && OR && OL->getDecl() == OR->getDecl()) { + DeclRefExpr *RL = dyn_cast(OL->getBase()->IgnoreImpCasts()); + DeclRefExpr *RR = dyn_cast(OR->getBase()->IgnoreImpCasts()); + if (RL && RR && RL->getDecl() == RR->getDecl()) + Sema.Diag(Loc, diag::warn_identity_field_assign) << 1; } - - return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); } -ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, - SourceLocation BuiltinLoc, - SourceLocation RPLoc, - DeclContext *ParentContext) { - return new (Context) - SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); -} +// C99 6.5.16.1 +QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, + SourceLocation Loc, + QualType CompoundType, + BinaryOperatorKind Opc) { + assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)); -ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc, - StringLiteral *BinaryData) { - EmbedDataStorage *Data = new (Context) EmbedDataStorage; - Data->BinaryData = BinaryData; - return new (Context) - EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0, - Data->getDataElementCount()); -} + // Verify that LHS is a modifiable lvalue, and emit error if not. + if (CheckForModifiableLvalue(LHSExpr, Loc, *this)) + return QualType(); -static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, - const Expr *SrcExpr) { - if (!DstType->isFunctionPointerType() || - !SrcExpr->getType()->isFunctionType()) - return false; + QualType LHSType = LHSExpr->getType(); + QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() : + CompoundType; - auto *DRE = dyn_cast(SrcExpr->IgnoreParenImpCasts()); - if (!DRE) - return false; +/* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + auto *RecordTy = RHSType->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + // BoundsSafety prevents passing flexible array members by copy. + QualType DisplayType = CompoundType.isNull() + ? RHS.get()->IgnoreParenImpCasts()->getType() : CompoundType; + Diag(Loc, diag::err_flexible_array_member_passed_by_copy) + << DisplayType; + // recovery by continuing as if this worked + } + } + + if (getLangOpts().hasBoundsSafety() && RHS.isUsable()) { + // Even if this check fails don't return early to allow the best + // possible error recovery and to allow any subsequent diagnostics to + // work. + (void)BoundsSafetyCheckAssignmentToCountAttrPtr( + LHSType, RHS.get(), Sema::AA_Assigning, Loc, + [&LHSExpr]() -> std::string { + // In simple cases describe what is being assigned to + if (auto *DR = dyn_cast(LHSExpr->IgnoreParenCasts())) { + auto *II = DR->getDecl()->getDeclName().getAsIdentifierInfo(); + if (II) + return II->getName().str(); + } else if (auto *ME = + dyn_cast(LHSExpr->IgnoreParenCasts())) { + return ME->getMemberDecl()->getQualifiedNameAsString(); + } + return ""; + }); + } +/* TO_UPSTREAM(BoundsSafety) OFF */ - auto *FD = dyn_cast(DRE->getDecl()); - if (!FD) - return false; + // OpenCL v1.2 s6.1.1.1 p2: + // The half data type can only be used to declare a pointer to a buffer that + // contains half values + if (getLangOpts().OpenCL && + !getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()) && + LHSType->isHalfType()) { + Diag(Loc, diag::err_opencl_half_load_store) << 1 + << LHSType.getUnqualifiedType(); + return QualType(); + } - return !S.checkAddressOfFunctionIsAvailable(FD, - /*Complain=*/true, - SrcExpr->getBeginLoc()); -} + // WebAssembly tables can't be used on RHS of an assignment expression. + if (RHSType->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_art) << 0; + return QualType(); + } -bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, - SourceLocation Loc, - QualType DstType, QualType SrcType, - Expr *SrcExpr, AssignmentAction Action, - bool *Complained) { - if (Complained) - *Complained = false; + AssignConvertType ConvTy; + if (CompoundType.isNull()) { + Expr *RHSCheck = RHS.get(); - // Decode the result (notice that AST's are still created for extensions). - bool CheckInferredResultType = false; - bool isInvalid = false; - unsigned DiagKind = 0; - ConversionFixItGenerator ConvHints; - bool MayHaveConvFixit = false; - bool MayHaveFunctionDiff = false; - const ObjCInterfaceDecl *IFace = nullptr; - const ObjCProtocolDecl *PDecl = nullptr; + CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this); - switch (ConvTy) { - case Compatible: - DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr); - return false; - - case PointerToInt: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_pointer_int; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_pointer_int; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IntToPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_int_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_int_pointer; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatibleFunctionPointerStrict: - DiagKind = - diag::warn_typecheck_convert_incompatible_function_pointer_strict; - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatibleFunctionPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_function_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_function_pointer; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatiblePointer: - if (Action == AA_Passing_CFAudited) { - DiagKind = diag::err_arc_typecheck_convert_incompatible_pointer; - } else if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_pointer; - } - CheckInferredResultType = DstType->isObjCObjectPointerType() && - SrcType->isObjCObjectPointerType(); - if (CheckInferredResultType) { - SrcType = SrcType.getUnqualifiedType(); - DstType = DstType.getUnqualifiedType(); - } else { - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - } - MayHaveConvFixit = true; - break; - case IncompatiblePointerSign: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_pointer_sign; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_pointer_sign; - } - break; - case FunctionVoidPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_pointer_void_func; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_pointer_void_func; - } - break; - case IncompatiblePointerDiscardsQualifiers: { - // Perform array-to-pointer decay if necessary. - if (SrcType->isArrayType()) SrcType = Context.getArrayDecayedType(SrcType); + QualType LHSTy(LHSType); + ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS); + if (RHS.isInvalid()) + return QualType(); + // Special case of NSObject attributes on c-style pointer types. + if (ConvTy == IncompatiblePointer && + ((Context.isObjCNSObjectType(LHSType) && + RHSType->isObjCObjectPointerType()) || + (Context.isObjCNSObjectType(RHSType) && + LHSType->isObjCObjectPointerType()))) + ConvTy = Compatible; - isInvalid = true; + if (ConvTy == Compatible && + LHSType->isObjCObjectType()) + Diag(Loc, diag::err_objc_object_assignment) + << LHSType; - Qualifiers lhq = SrcType->getPointeeType().getQualifiers(); - Qualifiers rhq = DstType->getPointeeType().getQualifiers(); - if (lhq.getAddressSpace() != rhq.getAddressSpace()) { - DiagKind = diag::err_typecheck_incompatible_address_space; - break; - } else if (lhq.getPointerAuth() != rhq.getPointerAuth()) { - DiagKind = diag::err_typecheck_incompatible_ptrauth; - break; - } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { - DiagKind = diag::err_typecheck_incompatible_ownership; - break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (isCompatibleBoundsUnsafeAssignment(ConvTy) && + allowBoundsUnsafePointerAssignment(LHSExpr->getType(), RHS.get(), + Loc)) + ConvTy = Compatible; + if (!CheckDynamicBoundVariableEscape(LHSTy, RHS.get()) && + !allowBoundsUnsafePointerAssignment(LHSExpr->getType(), RHS.get(), + Loc)) + return QualType(); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - llvm_unreachable("unknown error case for discarding qualifiers!"); - // fallthrough - } - case CompatiblePointerDiscardsQualifiers: - // If the qualifiers lost were because we were applying the - // (deprecated) C++ conversion from a string literal to a char* - // (or wchar_t*), then there was no error (C++ 4.2p2). FIXME: - // Ideally, this check would be performed in - // checkPointerTypesForAssignment. However, that would require a - // bit of refactoring (so that the second argument is an - // expression, rather than a type), which should be done as part - // of a larger effort to fix checkPointerTypesForAssignment for - // C++ semantics. - if (getLangOpts().CPlusPlus && - IsStringLiteralToNonConstPointerConversion(SrcExpr, DstType)) - return false; - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_discards_qualifiers; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_discards_qualifiers; + // If the RHS is a unary plus or minus, check to see if they = and + are + // right next to each other. If so, the user may have typo'd "x =+ 4" + // instead of "x += 4". + if (ImplicitCastExpr *ICE = dyn_cast(RHSCheck)) + RHSCheck = ICE->getSubExpr(); + if (UnaryOperator *UO = dyn_cast(RHSCheck)) { + if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) && + Loc.isFileID() && UO->getOperatorLoc().isFileID() && + // Only if the two operators are exactly adjacent. + Loc.getLocWithOffset(1) == UO->getOperatorLoc() && + // And there is a space or other character before the subexpr of the + // unary +/-. We don't want to warn on "x=-1". + Loc.getLocWithOffset(2) != UO->getSubExpr()->getBeginLoc() && + UO->getSubExpr()->getBeginLoc().isFileID()) { + Diag(Loc, diag::warn_not_compound_assign) + << (UO->getOpcode() == UO_Plus ? "+" : "-") + << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); + } } - break; - case IncompatibleNestedPointerQualifiers: - if (getLangOpts().CPlusPlus) { - isInvalid = true; - DiagKind = diag::err_nested_pointer_qualifier_mismatch; - } else { - DiagKind = diag::ext_nested_pointer_qualifier_mismatch; - } - break; - case IncompatibleNestedPointerAddressSpaceMismatch: - DiagKind = diag::err_typecheck_incompatible_nested_address_space; - isInvalid = true; - break; - case IntToBlockPointer: - DiagKind = diag::err_int_to_block_pointer; - isInvalid = true; - break; - case IncompatibleBlockPointer: - DiagKind = diag::err_typecheck_convert_incompatible_block_pointer; - isInvalid = true; - break; - case IncompatibleObjCQualifiedId: { - if (SrcType->isObjCQualifiedIdType()) { - const ObjCObjectPointerType *srcOPT = - SrcType->castAs(); - for (auto *srcProto : srcOPT->quals()) { - PDecl = srcProto; - break; + if (ConvTy == Compatible) { + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) { + // Warn about retain cycles where a block captures the LHS, but + // not if the LHS is a simple variable into which the block is + // being stored...unless that variable can be captured by reference! + const Expr *InnerLHS = LHSExpr->IgnoreParenCasts(); + const DeclRefExpr *DRE = dyn_cast(InnerLHS); + if (!DRE || DRE->getDecl()->hasAttr()) + ObjC().checkRetainCycles(LHSExpr, RHS.get()); } - if (const ObjCInterfaceType *IFaceT = - DstType->castAs()->getInterfaceType()) - IFace = IFaceT->getDecl(); - } - else if (DstType->isObjCQualifiedIdType()) { - const ObjCObjectPointerType *dstOPT = - DstType->castAs(); - for (auto *dstProto : dstOPT->quals()) { - PDecl = dstProto; - break; + + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong || + LHSType.isNonWeakInMRRWithObjCWeak(Context)) { + // It is safe to assign a weak reference into a strong variable. + // Although this code can still have problems: + // id x = self.weakProp; + // id y = self.weakProp; + // we do not warn to warn spuriously when 'x' and 'y' are on separate + // paths through the function. This should be revisited if + // -Wrepeated-use-of-weak is made flow-sensitive. + // For ObjCWeak only, we do not warn if the assign is to a non-weak + // variable, which will be valid for the current autorelease scope. + if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, + RHS.get()->getBeginLoc())) + getCurFunction()->markSafeWeakUse(RHS.get()); + + } else if (getLangOpts().ObjCAutoRefCount || getLangOpts().ObjCWeak) { + checkUnsafeExprAssigns(Loc, LHSExpr, RHS.get()); } - if (const ObjCInterfaceType *IFaceT = - SrcType->castAs()->getInterfaceType()) - IFace = IFaceT->getDecl(); - } - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_incompatible_qualified_id; - isInvalid = true; - } else { - DiagKind = diag::warn_incompatible_qualified_id; } - break; + } else { + // Compound assignment "x += y" + ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); } - case IncompatibleVectors: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_incompatible_vectors; - isInvalid = true; - } else { - DiagKind = diag::warn_incompatible_vectors; - } - break; - case IncompatibleObjCWeakRef: - DiagKind = diag::err_arc_weak_unavailable_assign; - isInvalid = true; - break; - case Incompatible: - if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { - if (Complained) - *Complained = true; - return true; - } - DiagKind = diag::err_typecheck_convert_incompatible; - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - isInvalid = true; - MayHaveFunctionDiff = true; - break; + const ValueDecl *Assignee = nullptr; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Currently the Assignee is only needed for bounds-safety diagnostics + // so guard passing this information along in this mode. + if (LangOpts.BoundsSafety) { + // LHS is allowed to have parentheses so drop them before casting. + if (auto *DRE = dyn_cast(LHSExpr->IgnoreParens())) { + Assignee = DRE->getDecl(); + } else if (auto *ME = dyn_cast(LHSExpr->IgnoreParens())) { + Assignee = ME->getMemberDecl(); + } + // TODO(dliew): Support ArraySubscriptExpr here (rdar://115201001) } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - QualType FirstType, SecondType; - switch (Action) { - case AA_Assigning: - case AA_Initializing: - // The destination type comes first. - FirstType = DstType; - SecondType = SrcType; - break; - - case AA_Returning: - case AA_Passing: - case AA_Passing_CFAudited: - case AA_Converting: - case AA_Sending: - case AA_Casting: - // The source type comes first. - FirstType = SrcType; - SecondType = DstType; - break; - } - - PartialDiagnostic FDiag = PDiag(DiagKind); - AssignmentAction ActionForDiag = Action; - if (Action == AA_Passing_CFAudited) - ActionForDiag = AA_Passing; + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Upstream doesn't have `Assignee`. + if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, + RHS.get(), AA_Assigning, nullptr, Assignee)) + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return QualType(); - FDiag << FirstType << SecondType << ActionForDiag - << SrcExpr->getSourceRange(); + CheckForNullPointerDereference(*this, LHSExpr); - if (DiagKind == diag::ext_typecheck_convert_incompatible_pointer_sign || - DiagKind == diag::err_typecheck_convert_incompatible_pointer_sign) { - auto isPlainChar = [](const clang::Type *Type) { - return Type->isSpecificBuiltinType(BuiltinType::Char_S) || - Type->isSpecificBuiltinType(BuiltinType::Char_U); - }; - FDiag << (isPlainChar(FirstType->getPointeeOrArrayElementType()) || - isPlainChar(SecondType->getPointeeOrArrayElementType())); - } + AssignedEntity AE{LHSExpr}; + checkExprLifetime(*this, AE, RHS.get()); - // If we can fix the conversion, suggest the FixIts. - if (!ConvHints.isNull()) { - for (FixItHint &H : ConvHints.Hints) - FDiag << H; + if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) { + if (CompoundType.isNull()) { + // C++2a [expr.ass]p5: + // A simple-assignment whose left operand is of a volatile-qualified + // type is deprecated unless the assignment is either a discarded-value + // expression or an unevaluated operand + ExprEvalContexts.back().VolatileAssignmentLHSs.push_back(LHSExpr); + } } - if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); } - - if (MayHaveFunctionDiff) - HandleFunctionTypeMismatch(FDiag, SecondType, FirstType); - - Diag(Loc, FDiag); - if ((DiagKind == diag::warn_incompatible_qualified_id || - DiagKind == diag::err_incompatible_qualified_id) && - PDecl && IFace && !IFace->hasDefinition()) - Diag(IFace->getLocation(), diag::note_incomplete_class_and_qualified_id) - << IFace << PDecl; + // C11 6.5.16p3: The type of an assignment expression is the type of the + // left operand would have after lvalue conversion. + // C11 6.3.2.1p2: ...this is called lvalue conversion. If the lvalue has + // qualified type, the value has the unqualified version of the type of the + // lvalue; additionally, if the lvalue has atomic type, the value has the + // non-atomic version of the type of the lvalue. + // C++ 5.17p1: the type of the assignment expression is that of its left + // operand. + return getLangOpts().CPlusPlus ? LHSType : LHSType.getAtomicUnqualifiedType(); +} - if (SecondType == Context.OverloadTy) - NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression, - FirstType, /*TakingAddress=*/true); +// Scenarios to ignore if expression E is: +// 1. an explicit cast expression into void +// 2. a function call expression that returns void +static bool IgnoreCommaOperand(const Expr *E, const ASTContext &Context) { + E = E->IgnoreParens(); - if (CheckInferredResultType) - ObjC().EmitRelatedResultTypeNote(SrcExpr); + if (const CastExpr *CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_ToVoid) { + return true; + } - if (Action == AA_Returning && ConvTy == IncompatiblePointer) - ObjC().EmitRelatedResultTypeNoteForReturn(DstType); + // static_cast on a dependent type will not show up as CK_ToVoid. + if (CE->getCastKind() == CK_Dependent && E->getType()->isVoidType() && + CE->getSubExpr()->getType()->isDependentType()) { + return true; + } + } - if (Complained) - *Complained = true; - return isInvalid; + if (const auto *CE = dyn_cast(E)) + return CE->getCallReturnType(Context)->isVoidType(); + return false; } -ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, - llvm::APSInt *Result, - AllowFoldKind CanFold) { - class SimpleICEDiagnoser : public VerifyICEDiagnoser { - public: - SemaDiagnosticBuilder diagnoseNotICEType(Sema &S, SourceLocation Loc, - QualType T) override { - return S.Diag(Loc, diag::err_ice_not_integral) - << T << S.LangOpts.CPlusPlus; - } - SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { - return S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus; - } - } Diagnoser; +void Sema::DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc) { + // No warnings in macros + if (Loc.isMacroID()) + return; - return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); -} + // Don't warn in template instantiations. + if (inTemplateInstantiation()) + return; -ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, - llvm::APSInt *Result, - unsigned DiagID, - AllowFoldKind CanFold) { - class IDDiagnoser : public VerifyICEDiagnoser { - unsigned DiagID; + // Scope isn't fine-grained enough to explicitly list the specific cases, so + // instead, skip more than needed, then call back into here with the + // CommaVisitor in SemaStmt.cpp. + // The listed locations are the initialization and increment portions + // of a for loop. The additional checks are on the condition of + // if statements, do/while loops, and for loops. + // Differences in scope flags for C89 mode requires the extra logic. + const unsigned ForIncrementFlags = + getLangOpts().C99 || getLangOpts().CPlusPlus + ? Scope::ControlScope | Scope::ContinueScope | Scope::BreakScope + : Scope::ContinueScope | Scope::BreakScope; + const unsigned ForInitFlags = Scope::ControlScope | Scope::DeclScope; + const unsigned ScopeFlags = getCurScope()->getFlags(); + if ((ScopeFlags & ForIncrementFlags) == ForIncrementFlags || + (ScopeFlags & ForInitFlags) == ForInitFlags) + return; - public: - IDDiagnoser(unsigned DiagID) - : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { } + // If there are multiple comma operators used together, get the RHS of the + // of the comma operator as the LHS. + while (const BinaryOperator *BO = dyn_cast(LHS)) { + if (BO->getOpcode() != BO_Comma) + break; + LHS = BO->getRHS(); + } - SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { - return S.Diag(Loc, DiagID); - } - } Diagnoser(DiagID); + // Only allow some expressions on LHS to not warn. + if (IgnoreCommaOperand(LHS, Context)) + return; - return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); + Diag(Loc, diag::warn_comma_operator); + Diag(LHS->getBeginLoc(), diag::note_cast_to_void) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(LHS->getBeginLoc(), + LangOpts.CPlusPlus ? "static_cast(" + : "(void)(") + << FixItHint::CreateInsertion(PP.getLocForEndOfToken(LHS->getEndLoc()), + ")"); } -Sema::SemaDiagnosticBuilder -Sema::VerifyICEDiagnoser::diagnoseNotICEType(Sema &S, SourceLocation Loc, - QualType T) { - return diagnoseNotICE(S, Loc); -} +// C99 6.5.17 +static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc) { + LHS = S.CheckPlaceholderExpr(LHS.get()); + RHS = S.CheckPlaceholderExpr(RHS.get()); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); -Sema::SemaDiagnosticBuilder -Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc) { - return S.Diag(Loc, diag::ext_expr_not_ice) << S.LangOpts.CPlusPlus; -} + // C's comma performs lvalue conversion (C99 6.3.2.1) on both its + // operands, but not unary promotions. + // C++'s comma does not do any conversions at all (C++ [expr.comma]p1). -ExprResult -Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, - VerifyICEDiagnoser &Diagnoser, - AllowFoldKind CanFold) { - SourceLocation DiagLoc = E->getBeginLoc(); + // So we treat the LHS as a ignored value, and in C++ we allow the + // containing site to determine what should be done with the RHS. + LHS = S.IgnoredValueConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); - if (getLangOpts().CPlusPlus11) { - // C++11 [expr.const]p5: - // If an expression of literal class type is used in a context where an - // integral constant expression is required, then that class type shall - // have a single non-explicit conversion function to an integral or - // unscoped enumeration type - ExprResult Converted; - class CXX11ConvertDiagnoser : public ICEConvertDiagnoser { - VerifyICEDiagnoser &BaseDiagnoser; - public: - CXX11ConvertDiagnoser(VerifyICEDiagnoser &BaseDiagnoser) - : ICEConvertDiagnoser(/*AllowScopedEnumerations*/ false, - BaseDiagnoser.Suppress, true), - BaseDiagnoser(BaseDiagnoser) {} + S.DiagnoseUnusedExprResult(LHS.get(), diag::warn_unused_comma_left_operand); - SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, - QualType T) override { - return BaseDiagnoser.diagnoseNotICEType(S, Loc, T); - } + if (!S.getLangOpts().CPlusPlus) { + RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + if (!RHS.get()->getType()->isVoidType()) + S.RequireCompleteType(Loc, RHS.get()->getType(), + diag::err_incomplete_type); + } - SemaDiagnosticBuilder diagnoseIncomplete( - Sema &S, SourceLocation Loc, QualType T) override { - return S.Diag(Loc, diag::err_ice_incomplete_type) << T; - } + if (!S.getDiagnostics().isIgnored(diag::warn_comma_operator, Loc)) + S.DiagnoseCommaOperator(LHS.get(), Loc); - SemaDiagnosticBuilder diagnoseExplicitConv( - Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { - return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy; - } + return RHS.get()->getType(); +} - SemaDiagnosticBuilder noteExplicitConv( - Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { - return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) - << ConvTy->isEnumeralType() << ConvTy; - } +/// CheckIncrementDecrementOperand - unlike most "Check" methods, this routine +/// doesn't need to call UsualUnaryConversions or UsualArithmeticConversions. +static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, + ExprValueKind &VK, + ExprObjectKind &OK, + SourceLocation OpLoc, bool IsInc, + bool IsPrefix) { + QualType ResType = Op->getType(); + // Atomic types can be used for increment / decrement where the non-atomic + // versions can, so ignore the _Atomic() specifier for the purpose of + // checking. + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - SemaDiagnosticBuilder diagnoseAmbiguous( - Sema &S, SourceLocation Loc, QualType T) override { - return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T; - } + assert(!ResType.isNull() && "no type for increment/decrement expression"); - SemaDiagnosticBuilder noteAmbiguous( - Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { + if (S.getLangOpts().CPlusPlus && ResType->isBooleanType()) { + // Decrement of bool is not allowed. + if (!IsInc) { + S.Diag(OpLoc, diag::err_decrement_bool) << Op->getSourceRange(); + return QualType(); + } + // Increment of bool sets it to true, but is deprecated. + S.Diag(OpLoc, S.getLangOpts().CPlusPlus17 ? diag::ext_increment_bool + : diag::warn_increment_bool) + << Op->getSourceRange(); + } else if (S.getLangOpts().CPlusPlus && ResType->isEnumeralType()) { + // Error on enum increments and decrements in C++ mode + S.Diag(OpLoc, diag::err_increment_decrement_enum) << IsInc << ResType; + return QualType(); + } else if (ResType->isRealType()) { + // OK! + } else if (ResType->isPointerType()) { + // C99 6.5.2.4p2, 6.5.6p2 + if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) + return QualType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !checkArithmeticUnaryOpBoundsSafetyPointer(S, Op, IsInc)) + return QualType(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } else if (ResType->isObjCObjectPointerType()) { + // On modern runtimes, ObjC pointer arithmetic is forbidden. + // Otherwise, we just need a complete type. + if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || + checkArithmeticOnObjCPointer(S, OpLoc, Op)) + return QualType(); + } else if (ResType->isAnyComplexType()) { + // C99 does not support ++/-- on complex types, we allow as an extension. + S.Diag(OpLoc, S.getLangOpts().C2y ? diag::warn_c2y_compat_increment_complex + : diag::ext_c2y_increment_complex) + << IsInc << Op->getSourceRange(); + } else if (ResType->isPlaceholderType()) { + ExprResult PR = S.CheckPlaceholderExpr(Op); + if (PR.isInvalid()) return QualType(); + return CheckIncrementDecrementOperand(S, PR.get(), VK, OK, OpLoc, + IsInc, IsPrefix); + } else if (S.getLangOpts().AltiVec && ResType->isVectorType()) { + // OK! ( C/C++ Language Extensions for CBEA(Version 2.6) 10.3 ) + } else if (S.getLangOpts().ZVector && ResType->isVectorType() && + (ResType->castAs()->getVectorKind() != + VectorKind::AltiVecBool)) { + // The z vector extensions allow ++ and -- for non-bool vectors. + } else if (S.getLangOpts().OpenCL && ResType->isVectorType() && + ResType->castAs()->getElementType()->isIntegerType()) { + // OpenCL V1.2 6.3 says dec/inc ops operate on integer vector types. + } else { + S.Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement) + << ResType << int(IsInc) << Op->getSourceRange(); + return QualType(); + } + // At this point, we know we have a real, complex or pointer type. + // Now make sure the operand is a modifiable lvalue. + if (CheckForModifiableLvalue(Op, OpLoc, S)) + return QualType(); + if (S.getLangOpts().CPlusPlus20 && ResType.isVolatileQualified()) { + // C++2a [expr.pre.inc]p1, [expr.post.inc]p1: + // An operand with volatile-qualified type is deprecated + S.Diag(OpLoc, diag::warn_deprecated_increment_decrement_volatile) + << IsInc << ResType; + } + // In C++, a prefix increment is the same type as the operand. Otherwise + // (in C or with postfix), the increment is the unqualified type of the + // operand. + if (IsPrefix && S.getLangOpts().CPlusPlus) { + VK = VK_LValue; + OK = Op->getObjectKind(); + return ResType; + } else { + VK = VK_PRValue; + return ResType.getUnqualifiedType(); + } +} + +/// getPrimaryDecl - Helper function for CheckAddressOfOperand(). +/// This routine allows us to typecheck complex/recursive expressions +/// where the declaration is needed for type checking. We only need to +/// handle cases when the expression references a function designator +/// or is an lvalue. Here are some examples: +/// - &(x) => x +/// - &*****f => f for f a function designator. +/// - &s.xx => s +/// - &s.zz[1].yy -> s, if zz is an array +/// - *(x + 1) -> x, if x is an array +/// - &"123"[2] -> 0 +/// - & __real__ x -> x +/// +/// FIXME: We don't recurse to the RHS of a comma, nor handle pointers to +/// members. +static ValueDecl *getPrimaryDecl(Expr *E) { + switch (E->getStmtClass()) { + case Stmt::DeclRefExprClass: + return cast(E)->getDecl(); + case Stmt::MemberExprClass: + // If this is an arrow operator, the address is an offset from + // the base's value, so the object the base refers to is + // irrelevant. + if (cast(E)->isArrow()) + return nullptr; + // Otherwise, the expression refers to a part of the base + return getPrimaryDecl(cast(E)->getBase()); + case Stmt::ArraySubscriptExprClass: { + // FIXME: This code shouldn't be necessary! We should catch the implicit + // promotion of register arrays earlier. + Expr* Base = cast(E)->getBase(); + if (ImplicitCastExpr* ICE = dyn_cast(Base)) { + if (ICE->getSubExpr()->getType()->isArrayType()) + return getPrimaryDecl(ICE->getSubExpr()); + } + return nullptr; + } + case Stmt::UnaryOperatorClass: { + UnaryOperator *UO = cast(E); + + switch(UO->getOpcode()) { + case UO_Real: + case UO_Imag: + case UO_Extension: + return getPrimaryDecl(UO->getSubExpr()); + default: + return nullptr; + } + } + case Stmt::ParenExprClass: + return getPrimaryDecl(cast(E)->getSubExpr()); + case Stmt::ImplicitCastExprClass: + // If the result of an implicit cast is an l-value, we care about + // the sub-expression; otherwise, the result here doesn't matter. + return getPrimaryDecl(cast(E)->getSubExpr()); + case Stmt::CXXUuidofExprClass: + return cast(E)->getGuidDecl(); + default: + return nullptr; + } +} + +namespace { +enum { + AO_Bit_Field = 0, + AO_Vector_Element = 1, + AO_Property_Expansion = 2, + AO_Register_Variable = 3, + AO_Matrix_Element = 4, + AO_No_Error = 5 +}; +} +/// Diagnose invalid operand for address of operations. +/// +/// \param Type The type of operand which cannot have its address taken. +static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, + Expr *E, unsigned Type) { + S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange(); +} + +bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, + const Expr *Op, + const CXXMethodDecl *MD) { + const auto *DRE = cast(Op->IgnoreParens()); + + if (Op != DRE) + return Diag(OpLoc, diag::err_parens_pointer_member_function) + << Op->getSourceRange(); + + // Taking the address of a dtor is illegal per C++ [class.dtor]p2. + if (isa(MD)) + return Diag(OpLoc, diag::err_typecheck_addrof_dtor) + << DRE->getSourceRange(); + + if (DRE->getQualifier()) + return false; + + if (MD->getParent()->getName().empty()) + return Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange(); + + SmallString<32> Str; + StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str); + return Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange() + << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual); +} + +QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { + if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){ + if (PTy->getKind() == BuiltinType::Overload) { + Expr *E = OrigOp.get()->IgnoreParens(); + if (!isa(E)) { + assert(cast(E)->getOpcode() == UO_AddrOf); + Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof_addrof_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + OverloadExpr *Ovl = cast(E); + if (isa(Ovl)) + if (!ResolveSingleFunctionTemplateSpecialization(Ovl)) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + return Context.OverloadTy; + } + + if (PTy->getKind() == BuiltinType::UnknownAny) + return Context.UnknownAnyTy; + + if (PTy->getKind() == BuiltinType::BoundMember) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + OrigOp = CheckPlaceholderExpr(OrigOp.get()); + if (OrigOp.isInvalid()) return QualType(); + } + + if (OrigOp.get()->isTypeDependent()) + return Context.DependentTy; + + assert(!OrigOp.get()->hasPlaceholderType()); + + // Make sure to ignore parentheses in subsequent checks + Expr *op = OrigOp.get()->IgnoreParens(); + + // In OpenCL captures for blocks called as lambda functions + // are located in the private address space. Blocks used in + // enqueue_kernel can be located in a different address space + // depending on a vendor implementation. Thus preventing + // taking an address of the capture to avoid invalid AS casts. + if (LangOpts.OpenCL) { + auto* VarRef = dyn_cast(op); + if (VarRef && VarRef->refersToEnclosingVariableOrCapture()) { + Diag(op->getExprLoc(), diag::err_opencl_taking_address_capture); + return QualType(); + } + } + + if (getLangOpts().C99) { + // Implement C99-only parts of addressof rules. + if (UnaryOperator* uOp = dyn_cast(op)) { + if (uOp->getOpcode() == UO_Deref) + // Per C99 6.5.3.2, the address of a deref always returns a valid result + // (assuming the deref expression is valid). + return uOp->getSubExpr()->getType(); + } + // Technically, there should be a check for array subscript + // expressions here, but the result of one is always an lvalue anyway. + } + ValueDecl *dcl = getPrimaryDecl(op); + + if (auto *FD = dyn_cast_or_null(dcl)) + if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + op->getBeginLoc())) + return QualType(); + + Expr::LValueClassification lval = op->ClassifyLValue(Context); + unsigned AddressOfError = AO_No_Error; + + if (lval == Expr::LV_ClassTemporary || lval == Expr::LV_ArrayTemporary) { + bool sfinae = (bool)isSFINAEContext(); + Diag(OpLoc, isSFINAEContext() ? diag::err_typecheck_addrof_temporary + : diag::ext_typecheck_addrof_temporary) + << op->getType() << op->getSourceRange(); + if (sfinae) + return QualType(); + // Materialize the temporary as an lvalue so that we can take its address. + OrigOp = op = + CreateMaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); + } else if (isa(op)) { + return Context.getPointerType(op->getType()); + } else if (lval == Expr::LV_MemberFunction) { + // If it's an instance method, make a member pointer. + // The expression must have exactly the form &A::foo. + + // If the underlying expression isn't a decl ref, give up. + if (!isa(op)) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + DeclRefExpr *DRE = cast(op); + CXXMethodDecl *MD = cast(DRE->getDecl()); + + CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); + + QualType MPTy = Context.getMemberPointerType( + op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); + + if (getLangOpts().PointerAuthCalls && MD->isVirtual() && + !isUnevaluatedContext() && !MPTy->isDependentType()) { + // When pointer authentication is enabled, argument and return types of + // vitual member functions must be complete. This is because vitrual + // member function pointers are implemented using virtual dispatch + // thunks and the thunks cannot be emitted if the argument or return + // types are incomplete. + auto ReturnOrParamTypeIsIncomplete = [&](QualType T, + SourceLocation DeclRefLoc, + SourceLocation RetArgTypeLoc) { + if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { + Diag(DeclRefLoc, + diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); + Diag(RetArgTypeLoc, + diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) + << T; + return true; + } + return false; + }; + QualType RetTy = MD->getReturnType(); + bool IsIncomplete = + !RetTy->isVoidType() && + ReturnOrParamTypeIsIncomplete( + RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); + for (auto *PVD : MD->parameters()) + IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, + PVD->getBeginLoc()); + if (IsIncomplete) + return QualType(); + } + + // Under the MS ABI, lock down the inheritance model now. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + (void)isCompleteType(OpLoc, MPTy); + return MPTy; + } else if (lval != Expr::LV_Valid && lval != Expr::LV_IncompleteVoidType) { + // C99 6.5.3.2p1 + // The operand must be either an l-value or a function designator + if (!op->getType()->isFunctionType()) { + // Use a special diagnostic for loads from property references. + if (isa(op)) { + AddressOfError = AO_Property_Expansion; + } else { + Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) + << op->getType() << op->getSourceRange(); + return QualType(); + } + } else if (const auto *DRE = dyn_cast(op)) { + if (const auto *MD = dyn_cast_or_null(DRE->getDecl())) + CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); + } + + } else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1 + // The operand cannot be a bit-field + AddressOfError = AO_Bit_Field; + } else if (op->getObjectKind() == OK_VectorComponent) { + // The operand cannot be an element of a vector + AddressOfError = AO_Vector_Element; + } else if (op->getObjectKind() == OK_MatrixComponent) { + // The operand cannot be an element of a matrix. + AddressOfError = AO_Matrix_Element; + } else if (dcl) { // C99 6.5.3.2p1 + // We have an lvalue with a decl. Make sure the decl is not declared + // with the register storage-class specifier. + if (const VarDecl *vd = dyn_cast(dcl)) { + // in C++ it is not error to take address of a register + // variable (c++03 7.1.1P3) + if (vd->getStorageClass() == SC_Register && + !getLangOpts().CPlusPlus) { + AddressOfError = AO_Register_Variable; + } + } else if (isa(dcl)) { + AddressOfError = AO_Property_Expansion; + } else if (isa(dcl)) { + return Context.OverloadTy; + } else if (isa(dcl) || isa(dcl)) { + // Okay: we can take the address of a field. + // Could be a pointer to member, though, if there is an explicit + // scope qualifier for the class. + + // [C++26] [expr.prim.id.general] + // If an id-expression E denotes a non-static non-type member + // of some class C [...] and if E is a qualified-id, E is + // not the un-parenthesized operand of the unary & operator [...] + // the id-expression is transformed into a class member access expression. + if (isa(op) && cast(op)->getQualifier() && + !isa(OrigOp.get())) { + DeclContext *Ctx = dcl->getDeclContext(); + if (Ctx && Ctx->isRecord()) { + if (dcl->getType()->isReferenceType()) { + Diag(OpLoc, + diag::err_cannot_form_pointer_to_member_of_reference_type) + << dcl->getDeclName() << dcl->getType(); + return QualType(); + } + + while (cast(Ctx)->isAnonymousStructOrUnion()) + Ctx = Ctx->getParent(); + + QualType MPTy = Context.getMemberPointerType( + op->getType(), + Context.getTypeDeclType(cast(Ctx)).getTypePtr()); + // Under the MS ABI, lock down the inheritance model now. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + (void)isCompleteType(OpLoc, MPTy); + return MPTy; + } + } + } else if (!isa(dcl)) + llvm_unreachable("Unknown/unexpected decl type"); + } + + if (AddressOfError != AO_No_Error) { + diagnoseAddressOfInvalidType(*this, OpLoc, op, AddressOfError); + return QualType(); + } + + if (lval == Expr::LV_IncompleteVoidType) { + // Taking the address of a void variable is technically illegal, but we + // allow it in cases which are otherwise valid. + // Example: "extern void x; void* y = &x;". + Diag(OpLoc, diag::ext_typecheck_addrof_void) << op->getSourceRange(); + } + + // If the operand has type "type", the result has type "pointer to type". + if (op->getType()->isObjCObjectType()) + return Context.getObjCObjectPointerType(op->getType()); + + // Cannot take the address of WebAssembly references or tables. + if (Context.getTargetInfo().getTriple().isWasm()) { + QualType OpTy = op->getType(); + if (OpTy.isWebAssemblyReferenceType()) { + Diag(OpLoc, diag::err_wasm_ca_reference) + << 1 << OrigOp.get()->getSourceRange(); + return QualType(); + } + if (OpTy->isWebAssemblyTableType()) { + Diag(OpLoc, diag::err_wasm_table_pr) + << 1 << OrigOp.get()->getSourceRange(); + return QualType(); + } + } + + CheckAddressOfPackedMember(op); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (auto ME = dyn_cast(op)) { + auto FD = ME->getMemberDecl(); + if (auto *Att = FD->getAttr()) { + assert(Att->dependerDecls_size() > 0); + const RecordDecl *FlexBase = + DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey( + op, /*OS*/ nullptr); + if (FlexBase) { + for (auto DepDecl : Att->dependerDecls()) { + // Only emit error if this field is being accessed from a struct + // with a flexible array member. If this is a nested struct field, + // it could be used separately in a context without FAMs, and in + // that case we should allow taking its address. + if (!FlexBase->isParentStructOf(DepDecl)) + continue; + ValueDecl *FAMDecl = cast(DepDecl); + if (auto DCPTy = FAMDecl->getType()->getAs()) { + assert(DCPTy->isIncompleteArrayType() && + "expected flexible array member"); + Diag( + OpLoc, + diag:: + err_bounds_safety_unsupported_address_of_incomplete_array_count) + << op->getSourceRange(); + auto CountExpr = DCPTy->getCountExpr(); + Diag(CountExpr->getExprLoc(), + diag::note_bounds_safety_count_param_loc) + << CountExpr->getSourceRange(); + // This results in RecoveryExpr, preventing double errors when the + // expression is reconstructed + return QualType(); + } + } + } + } + } + } + + // BoundsSafety: taking an address returns a bound pointer to allow bounds to be + // propagated in case the target has bounds, this can be the case with or + // without auto-typing of variables. + auto GetPointerBoundAttributes = [&](Expr *E) -> BoundsSafetyPointerAttributes { + BoundsSafetyPointerAttributes FA; + if (auto ASE = dyn_cast(E)) { + if (auto PT = ASE->getBase()->getType()->getAs()) { + FA = PT->getPointerAttributes(); + } + } else if (getLangOpts().BoundsSafety) { + // Taking the address of an object with a meaningless size results in a + // __single pointer. + if (E->getType()->isSizeMeaningless()) { + FA.setSingle(); + } else { + FA.setBidiIndexable(); + } + } + if (E->getType()->isCountAttributedType()) { + if (const auto *AT = Context.getAsIncompleteArrayType(E->getType())) { + Diag(OpLoc, diag::err_bounds_safety_unsupported_address_of_incomplete_array) + << op->getSourceRange(); + Diag(OpLoc, diag::note_bounds_safety_remove_address_of_operator) + << Context.getPointerType(AT->getElementType()) + << Context.getPointerType(OrigOp.get()->getType()) + << FixItHint::CreateRemoval(op->getSourceRange()); + // recover by using __bidi_indexable bounds, which are less likely + // to cause spurious warnings + FA.setBidiIndexable(); + } + } + return FA; + }; + BoundsSafetyPointerAttributes FA = GetPointerBoundAttributes(op); + QualType PT = Context.getPointerType(op->getType(), FA); + if (const auto *ASE = dyn_cast(op)) { + if (const auto *VTT = + ASE->getBase()->getType()->getAs()) { + return Context.getValueTerminatedType(PT, VTT->getTerminatorExpr()); + } + } else if (getLangOpts().BoundsSafety) { + PT = Context.getAttributedType(attr::PtrAutoAttr, PT, PT); + } + return PT; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + return Context.getPointerType(op->getType()); +} + +static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) { + const DeclRefExpr *DRE = dyn_cast(Exp); + if (!DRE) + return; + const Decl *D = DRE->getDecl(); + if (!D) + return; + const ParmVarDecl *Param = dyn_cast(D); + if (!Param) + return; + if (const FunctionDecl* FD = dyn_cast(Param->getDeclContext())) + if (!FD->hasAttr() && !Param->hasAttr()) + return; + if (FunctionScopeInfo *FD = S.getCurFunction()) + FD->ModifiedNonNullParams.insert(Param); +} + +/// CheckIndirectionOperand - Type check unary indirection (prefix '*'). +static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, + SourceLocation OpLoc, + bool IsAfterAmp = false) { + ExprResult ConvResult = S.UsualUnaryConversions(Op); + if (ConvResult.isInvalid()) + return QualType(); + Op = ConvResult.get(); + QualType OpTy = Op->getType(); + QualType Result; + + if (isa(Op)) { + QualType OpOrigType = Op->IgnoreParenCasts()->getType(); + S.CheckCompatibleReinterpretCast(OpOrigType, OpTy, /*IsDereference*/true, + Op->getSourceRange()); + } + + if (const PointerType *PT = OpTy->getAs()) + { + Result = PT->getPointeeType(); + } + else if (const ObjCObjectPointerType *OPT = + OpTy->getAs()) + Result = OPT->getPointeeType(); + else { + ExprResult PR = S.CheckPlaceholderExpr(Op); + if (PR.isInvalid()) return QualType(); + if (PR.get() != Op) + return CheckIndirectionOperand(S, PR.get(), VK, OpLoc); + } + + if (Result.isNull()) { + S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer) + << OpTy << Op->getSourceRange(); + return QualType(); + } + + if (Result->isVoidType()) { + // C++ [expr.unary.op]p1: + // [...] the expression to which [the unary * operator] is applied shall + // be a pointer to an object type, or a pointer to a function type + LangOptions LO = S.getLangOpts(); + if (LO.CPlusPlus) + S.Diag(OpLoc, diag::err_typecheck_indirection_through_void_pointer_cpp) + << OpTy << Op->getSourceRange(); + else if (!(LO.C99 && IsAfterAmp) && !S.isUnevaluatedContext()) + S.Diag(OpLoc, diag::ext_typecheck_indirection_through_void_pointer) + << OpTy << Op->getSourceRange(); + } + + // Dereferences are usually l-values... + VK = VK_LValue; + + // ...except that certain expressions are never l-values in C. + if (!S.getLangOpts().CPlusPlus && Result.isCForbiddenLValueType()) + VK = VK_PRValue; + + return Result; +} + +BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) { + BinaryOperatorKind Opc; + switch (Kind) { + default: llvm_unreachable("Unknown binop!"); + case tok::periodstar: Opc = BO_PtrMemD; break; + case tok::arrowstar: Opc = BO_PtrMemI; break; + case tok::star: Opc = BO_Mul; break; + case tok::slash: Opc = BO_Div; break; + case tok::percent: Opc = BO_Rem; break; + case tok::plus: Opc = BO_Add; break; + case tok::minus: Opc = BO_Sub; break; + case tok::lessless: Opc = BO_Shl; break; + case tok::greatergreater: Opc = BO_Shr; break; + case tok::lessequal: Opc = BO_LE; break; + case tok::less: Opc = BO_LT; break; + case tok::greaterequal: Opc = BO_GE; break; + case tok::greater: Opc = BO_GT; break; + case tok::exclaimequal: Opc = BO_NE; break; + case tok::equalequal: Opc = BO_EQ; break; + case tok::spaceship: Opc = BO_Cmp; break; + case tok::amp: Opc = BO_And; break; + case tok::caret: Opc = BO_Xor; break; + case tok::pipe: Opc = BO_Or; break; + case tok::ampamp: Opc = BO_LAnd; break; + case tok::pipepipe: Opc = BO_LOr; break; + case tok::equal: Opc = BO_Assign; break; + case tok::starequal: Opc = BO_MulAssign; break; + case tok::slashequal: Opc = BO_DivAssign; break; + case tok::percentequal: Opc = BO_RemAssign; break; + case tok::plusequal: Opc = BO_AddAssign; break; + case tok::minusequal: Opc = BO_SubAssign; break; + case tok::lesslessequal: Opc = BO_ShlAssign; break; + case tok::greatergreaterequal: Opc = BO_ShrAssign; break; + case tok::ampequal: Opc = BO_AndAssign; break; + case tok::caretequal: Opc = BO_XorAssign; break; + case tok::pipeequal: Opc = BO_OrAssign; break; + case tok::comma: Opc = BO_Comma; break; + } + return Opc; +} + +static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( + tok::TokenKind Kind) { + UnaryOperatorKind Opc; + switch (Kind) { + default: llvm_unreachable("Unknown unary op!"); + case tok::plusplus: Opc = UO_PreInc; break; + case tok::minusminus: Opc = UO_PreDec; break; + case tok::amp: Opc = UO_AddrOf; break; + case tok::star: Opc = UO_Deref; break; + case tok::plus: Opc = UO_Plus; break; + case tok::minus: Opc = UO_Minus; break; + case tok::tilde: Opc = UO_Not; break; + case tok::exclaim: Opc = UO_LNot; break; + case tok::kw___real: Opc = UO_Real; break; + case tok::kw___imag: Opc = UO_Imag; break; + case tok::kw___extension__: Opc = UO_Extension; break; + } + return Opc; +} + +const FieldDecl * +Sema::getSelfAssignmentClassMemberCandidate(const ValueDecl *SelfAssigned) { + // Explore the case for adding 'this->' to the LHS of a self assignment, very + // common for setters. + // struct A { + // int X; + // -void setX(int X) { X = X; } + // +void setX(int X) { this->X = X; } + // }; + + // Only consider parameters for self assignment fixes. + if (!isa(SelfAssigned)) + return nullptr; + const auto *Method = + dyn_cast_or_null(getCurFunctionDecl(true)); + if (!Method) + return nullptr; + + const CXXRecordDecl *Parent = Method->getParent(); + // In theory this is fixable if the lambda explicitly captures this, but + // that's added complexity that's rarely going to be used. + if (Parent->isLambda()) + return nullptr; + + // FIXME: Use an actual Lookup operation instead of just traversing fields + // in order to get base class fields. + auto Field = + llvm::find_if(Parent->fields(), + [Name(SelfAssigned->getDeclName())](const FieldDecl *F) { + return F->getDeclName() == Name; + }); + return (Field != Parent->field_end()) ? *Field : nullptr; +} + +/// DiagnoseSelfAssignment - Emits a warning if a value is assigned to itself. +/// This warning suppressed in the event of macro expansions. +static void DiagnoseSelfAssignment(Sema &S, Expr *LHSExpr, Expr *RHSExpr, + SourceLocation OpLoc, bool IsBuiltin) { + if (S.inTemplateInstantiation()) + return; + if (S.isUnevaluatedContext()) + return; + if (OpLoc.isInvalid() || OpLoc.isMacroID()) + return; + LHSExpr = LHSExpr->IgnoreParenImpCasts(); + RHSExpr = RHSExpr->IgnoreParenImpCasts(); + const DeclRefExpr *LHSDeclRef = dyn_cast(LHSExpr); + const DeclRefExpr *RHSDeclRef = dyn_cast(RHSExpr); + if (!LHSDeclRef || !RHSDeclRef || + LHSDeclRef->getLocation().isMacroID() || + RHSDeclRef->getLocation().isMacroID()) + return; + const ValueDecl *LHSDecl = + cast(LHSDeclRef->getDecl()->getCanonicalDecl()); + const ValueDecl *RHSDecl = + cast(RHSDeclRef->getDecl()->getCanonicalDecl()); + if (LHSDecl != RHSDecl) + return; + if (LHSDecl->getType().isVolatileQualified()) + return; + if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) + if (RefTy->getPointeeType().isVolatileQualified()) + return; + + // Do not warn about self-assignments for dynamic-range pointer types, or + // for declarations that they depend on. + if (LHSDecl->getType()->isBoundsAttributedType()) + return; + if (auto *Attr = LHSDecl->getAttr()) + if (!Attr->getIsDeref()) + return; + + auto Diag = S.Diag(OpLoc, IsBuiltin ? diag::warn_self_assignment_builtin + : diag::warn_self_assignment_overloaded) + << LHSDeclRef->getType() << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); + if (const FieldDecl *SelfAssignField = + S.getSelfAssignmentClassMemberCandidate(RHSDecl)) + Diag << 1 << SelfAssignField + << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->"); + else + Diag << 0; +} + +/// Check if a bitwise-& is performed on an Objective-C pointer. This +/// is usually indicative of introspection within the Objective-C pointer. +static void checkObjCPointerIntrospection(Sema &S, ExprResult &L, ExprResult &R, + SourceLocation OpLoc) { + if (!S.getLangOpts().ObjC) + return; + + const Expr *ObjCPointerExpr = nullptr, *OtherExpr = nullptr; + const Expr *LHS = L.get(); + const Expr *RHS = R.get(); + + if (LHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = LHS; + OtherExpr = RHS; + } + else if (RHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = RHS; + OtherExpr = LHS; + } + + // This warning is deliberately made very specific to reduce false + // positives with logic that uses '&' for hashing. This logic mainly + // looks for code trying to introspect into tagged pointers, which + // code should generally never do. + if (ObjCPointerExpr && isa(OtherExpr->IgnoreParenCasts())) { + unsigned Diag = diag::warn_objc_pointer_masking; + // Determine if we are introspecting the result of performSelectorXXX. + const Expr *Ex = ObjCPointerExpr->IgnoreParenCasts(); + // Special case messages to -performSelector and friends, which + // can return non-pointer values boxed in a pointer value. + // Some clients may wish to silence warnings in this subcase. + if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { + Selector S = ME->getSelector(); + StringRef SelArg0 = S.getNameForSlot(0); + if (SelArg0.starts_with("performSelector")) + Diag = diag::warn_objc_pointer_masking_performSelector; + } + + S.Diag(OpLoc, Diag) + << ObjCPointerExpr->getSourceRange(); + } +} + +static NamedDecl *getDeclFromExpr(Expr *E) { + if (!E) + return nullptr; + if (auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + if (auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (auto *IRE = dyn_cast(E)) + return IRE->getDecl(); + return nullptr; +} + +// This helper function promotes a binary operator's operands (which are of a +// half vector type) to a vector of floats and then truncates the result to +// a vector of either half or short. +static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, + BinaryOperatorKind Opc, QualType ResultTy, + ExprValueKind VK, ExprObjectKind OK, + bool IsCompAssign, SourceLocation OpLoc, + FPOptionsOverride FPFeatures) { + auto &Context = S.getASTContext(); + assert((isVector(ResultTy, Context.HalfTy) || + isVector(ResultTy, Context.ShortTy)) && + "Result must be a vector of half or short"); + assert(isVector(LHS.get()->getType(), Context.HalfTy) && + isVector(RHS.get()->getType(), Context.HalfTy) && + "both operands expected to be a half vector"); + + RHS = convertVector(RHS.get(), Context.FloatTy, S); + QualType BinOpResTy = RHS.get()->getType(); + + // If Opc is a comparison, ResultType is a vector of shorts. In that case, + // change BinOpResTy to a vector of ints. + if (isVector(ResultTy, Context.ShortTy)) + BinOpResTy = S.GetSignedVectorType(BinOpResTy); + + if (IsCompAssign) + return CompoundAssignOperator::Create(Context, LHS.get(), RHS.get(), Opc, + ResultTy, VK, OK, OpLoc, FPFeatures, + BinOpResTy, BinOpResTy); + + LHS = convertVector(LHS.get(), Context.FloatTy, S); + auto *BO = BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, + BinOpResTy, VK, OK, OpLoc, FPFeatures); + return convertVector(BO, ResultTy->castAs()->getElementType(), S); +} + +static std::pair +CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr) { + ExprResult LHS = LHSExpr, RHS = RHSExpr; + if (!S.Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes on either side of a binop because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + LHS = S.CorrectDelayedTyposInExpr(LHS); + RHS = S.CorrectDelayedTyposInExpr( + RHS, /*InitDecl=*/nullptr, /*RecoverUncorrectedTypos=*/false, + [Opc, LHS](Expr *E) { + if (Opc != BO_Assign) + return ExprResult(E); + // Avoid correcting the RHS to the same Expr as the LHS. + Decl *D = getDeclFromExpr(E); + return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; + }); + } + return std::make_pair(LHS, RHS); +} + +/// Returns true if conversion between vectors of halfs and vectors of floats +/// is needed. +static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, + Expr *E0, Expr *E1 = nullptr) { + if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType || + Ctx.getTargetInfo().useFP16ConversionIntrinsics()) + return false; + + auto HasVectorOfHalfType = [&Ctx](Expr *E) { + QualType Ty = E->IgnoreImplicit()->getType(); + + // Don't promote half precision neon vectors like float16x4_t in arm_neon.h + // to vectors of floats. Although the element type of the vectors is __fp16, + // the vectors shouldn't be treated as storage-only types. See the + // discussion here: https://reviews.llvm.org/rG825235c140e7 + if (const VectorType *VT = Ty->getAs()) { + if (VT->getVectorKind() == VectorKind::Neon) + return false; + return VT->getElementType().getCanonicalType() == Ctx.HalfTy; + } + return false; + }; + + return HasVectorOfHalfType(E0) && (!E1 || HasVectorOfHalfType(E1)); +} + +ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHSExpr, Expr *RHSExpr) { + if (getLangOpts().CPlusPlus11 && isa(RHSExpr)) { + // The syntax only allows initializer lists on the RHS of assignment, + // so we don't need to worry about accepting invalid code for + // non-assignment operators. + // C++11 5.17p9: + // The meaning of x = {v} [...] is that of x = T(v) [...]. The meaning + // of x = {} is x = T(). + InitializationKind Kind = InitializationKind::CreateDirectList( + RHSExpr->getBeginLoc(), RHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(LHSExpr->getType()); + InitializationSequence InitSeq(*this, Entity, Kind, RHSExpr); + ExprResult Init = InitSeq.Perform(*this, Entity, Kind, RHSExpr); + if (Init.isInvalid()) + return Init; + RHSExpr = Init.get(); + } + + ExprResult LHS = LHSExpr, RHS = RHSExpr; + QualType ResultTy; // Result type of the binary operator. + // The following two variables are used for compound assignment operators + QualType CompLHSTy; // Type of LHS after promotions for computation + QualType CompResultTy; // Type of computation result + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + bool ConvertHalfVec = false; + + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); + + if (getLangOpts().OpenCL) { + QualType LHSTy = LHSExpr->getType(); + QualType RHSTy = RHSExpr->getType(); + // OpenCLC v2.0 s6.13.11.1 allows atomic variables to be initialized by + // the ATOMIC_VAR_INIT macro. + if (LHSTy->isAtomicType() || RHSTy->isAtomicType()) { + SourceRange SR(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + if (BO_Assign == Opc) + Diag(OpLoc, diag::err_opencl_atomic_init) << 0 << SR; + else + ResultTy = InvalidOperands(OpLoc, LHS, RHS); + return ExprError(); + } + + // OpenCL special types - image, sampler, pipe, and blocks are to be used + // only with a builtin functions and therefore should be disallowed here. + if (LHSTy->isImageType() || RHSTy->isImageType() || + LHSTy->isSamplerT() || RHSTy->isSamplerT() || + LHSTy->isPipeType() || RHSTy->isPipeType() || + LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) { + ResultTy = InvalidOperands(OpLoc, LHS, RHS); + return ExprError(); + } + } + + 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; + /* TO_UPSTREAM(BoundsSafety) ON*/ + ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + 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); + + if (const auto *BI = dyn_cast(LHSExpr); + BI && BI->isComparisonOp()) + Diag(OpLoc, diag::warn_consecutive_comparison); + + 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); + [[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; + /* TO_UPSTREAM(BoundsSafety) ON*/ + CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + 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); + [[fallthrough]]; + case BO_XorAssign: + CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_Comma: + ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); + if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { + VK = RHS.get()->getValueKind(); + OK = RHS.get()->getObjectKind(); + } + break; + } + if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) + return ExprError(); + + // Some of the binary operations require promoting operands of half vector to + // float vectors and truncating the result back to half vector. For now, we do + // this only when HalfArgsAndReturn is set (that is, when the target is arm or + // arm64). + assert( + (Opc == BO_Comma || isVector(RHS.get()->getType(), Context.HalfTy) == + isVector(LHS.get()->getType(), Context.HalfTy)) && + "both sides are half vectors or neither sides are"); + ConvertHalfVec = + needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get()); + + // Check for array bounds violations for both sides of the BinaryOperator + CheckArrayAccess(LHS.get()); + CheckArrayAccess(RHS.get()); + + if (const ObjCIsaExpr *OISA = dyn_cast(LHS.get()->IgnoreParenCasts())) { + NamedDecl *ObjectSetClass = LookupSingleName(TUScope, + &Context.Idents.get("object_setClass"), + SourceLocation(), LookupOrdinaryName); + if (ObjectSetClass && isa(LHS.get())) { + SourceLocation RHSLocEnd = getLocForEndOfToken(RHS.get()->getEndLoc()); + Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign) + << FixItHint::CreateInsertion(LHS.get()->getBeginLoc(), + "object_setClass(") + << FixItHint::CreateReplacement(SourceRange(OISA->getOpLoc(), OpLoc), + ",") + << FixItHint::CreateInsertion(RHSLocEnd, ")"); + } + else + Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign); + } + else if (const ObjCIvarRefExpr *OIRE = + dyn_cast(LHS.get()->IgnoreParenCasts())) + DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get()); + + // Opc is not a compound assignment if CompResultTy is null. + if (CompResultTy.isNull()) { + if (ConvertHalfVec) + return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false, + OpLoc, CurFPFeatureOverrides()); + return BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, ResultTy, + VK, OK, OpLoc, CurFPFeatureOverrides()); + } + + // Handle compound assignments. + if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() != + OK_ObjCProperty) { + VK = VK_LValue; + OK = LHS.get()->getObjectKind(); + } + + // The LHS is not converted to the result type for fixed-point compound + // assignment as the common type is computed on demand. Reset the CompLHSTy + // to the LHS type we would have gotten after unary conversions. + if (CompResultTy->isFixedPointType()) + CompLHSTy = UsualUnaryConversions(LHS.get()).get()->getType(); + + if (ConvertHalfVec) + return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true, + OpLoc, CurFPFeatureOverrides()); + + return CompoundAssignOperator::Create( + Context, LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, + CurFPFeatureOverrides(), CompLHSTy, CompResultTy); +} + +/// DiagnoseBitwisePrecedence - Emit a warning when bitwise and comparison +/// operators are mixed in a way that suggests that the programmer forgot that +/// comparison operators have higher precedence. The most typical example of +/// such code is "flags & 0x0020 != 0", which is equivalent to "flags & 1". +static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *LHSExpr, + Expr *RHSExpr) { + BinaryOperator *LHSBO = dyn_cast(LHSExpr); + BinaryOperator *RHSBO = dyn_cast(RHSExpr); + + // Check that one of the sides is a comparison operator and the other isn't. + bool isLeftComp = LHSBO && LHSBO->isComparisonOp(); + bool isRightComp = RHSBO && RHSBO->isComparisonOp(); + if (isLeftComp == isRightComp) + return; + + // Bitwise operations are sometimes used as eager logical ops. + // Don't diagnose this. + bool isLeftBitwise = LHSBO && LHSBO->isBitwiseOp(); + bool isRightBitwise = RHSBO && RHSBO->isBitwiseOp(); + if (isLeftBitwise || isRightBitwise) + return; + + SourceRange DiagRange = isLeftComp + ? SourceRange(LHSExpr->getBeginLoc(), OpLoc) + : SourceRange(OpLoc, RHSExpr->getEndLoc()); + StringRef OpStr = isLeftComp ? LHSBO->getOpcodeStr() : RHSBO->getOpcodeStr(); + SourceRange ParensRange = + isLeftComp + ? SourceRange(LHSBO->getRHS()->getBeginLoc(), RHSExpr->getEndLoc()) + : SourceRange(LHSExpr->getBeginLoc(), RHSBO->getLHS()->getEndLoc()); + + Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) + << DiagRange << BinaryOperator::getOpcodeStr(Opc) << OpStr; + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_silence) << OpStr, + (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange()); + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_bitwise_first) + << BinaryOperator::getOpcodeStr(Opc), + ParensRange); +} + +/// It accepts a '&&' expr that is inside a '||' one. +/// Emit a diagnostic together with a fixit hint that wraps the '&&' expression +/// in parentheses. +static void +EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, + BinaryOperator *Bop) { + assert(Bop->getOpcode() == BO_LAnd); + Self.Diag(Bop->getOperatorLoc(), diag::warn_logical_and_in_logical_or) + << Bop->getSourceRange() << OpLoc; + SuggestParentheses(Self, Bop->getOperatorLoc(), + Self.PDiag(diag::note_precedence_silence) + << Bop->getOpcodeStr(), + Bop->getSourceRange()); +} + +/// Look for '&&' in the left hand of a '||' expr. +static void DiagnoseLogicalAndInLogicalOrLHS(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + if (BinaryOperator *Bop = dyn_cast(LHSExpr)) { + if (Bop->getOpcode() == BO_LAnd) { + // If it's "string_literal && a || b" don't warn since the precedence + // doesn't matter. + if (!isa(Bop->getLHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + } else if (Bop->getOpcode() == BO_LOr) { + if (BinaryOperator *RBop = dyn_cast(Bop->getRHS())) { + // If it's "a || b && string_literal || c" we didn't warn earlier for + // "a || b && string_literal", but warn now. + if (RBop->getOpcode() == BO_LAnd && + isa(RBop->getRHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, RBop); + } + } + } +} + +/// Look for '&&' in the right hand of a '||' expr. +static void DiagnoseLogicalAndInLogicalOrRHS(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + if (BinaryOperator *Bop = dyn_cast(RHSExpr)) { + if (Bop->getOpcode() == BO_LAnd) { + // If it's "a || b && string_literal" don't warn since the precedence + // doesn't matter. + if (!isa(Bop->getRHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + } + } +} + +/// Look for bitwise op in the left or right hand of a bitwise op with +/// lower precedence and emit a diagnostic together with a fixit hint that wraps +/// the '&' expression in parentheses. +static void DiagnoseBitwiseOpInBitwiseOp(Sema &S, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *SubExpr) { + if (BinaryOperator *Bop = dyn_cast(SubExpr)) { + if (Bop->isBitwiseOp() && Bop->getOpcode() < Opc) { + S.Diag(Bop->getOperatorLoc(), diag::warn_bitwise_op_in_bitwise_op) + << Bop->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc) + << Bop->getSourceRange() << OpLoc; + SuggestParentheses(S, Bop->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) + << Bop->getOpcodeStr(), + Bop->getSourceRange()); + } + } +} + +static void DiagnoseAdditionInShift(Sema &S, SourceLocation OpLoc, + Expr *SubExpr, StringRef Shift) { + if (BinaryOperator *Bop = dyn_cast(SubExpr)) { + if (Bop->getOpcode() == BO_Add || Bop->getOpcode() == BO_Sub) { + StringRef Op = Bop->getOpcodeStr(); + S.Diag(Bop->getOperatorLoc(), diag::warn_addition_in_bitshift) + << Bop->getSourceRange() << OpLoc << Shift << Op; + SuggestParentheses(S, Bop->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) << Op, + Bop->getSourceRange()); + } + } +} + +static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + CXXOperatorCallExpr *OCE = dyn_cast(LHSExpr); + if (!OCE) + return; + + FunctionDecl *FD = OCE->getDirectCallee(); + if (!FD || !FD->isOverloadedOperator()) + return; + + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind != OO_LessLess && Kind != OO_GreaterGreater) + return; + + S.Diag(OpLoc, diag::warn_overloaded_shift_in_comparison) + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange() + << (Kind == OO_LessLess); + SuggestParentheses(S, OCE->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) + << (Kind == OO_LessLess ? "<<" : ">>"), + OCE->getSourceRange()); + SuggestParentheses( + S, OpLoc, S.PDiag(diag::note_evaluate_comparison_first), + SourceRange(OCE->getArg(1)->getBeginLoc(), RHSExpr->getEndLoc())); +} + +/// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky +/// precedence. +static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *LHSExpr, + Expr *RHSExpr){ + // Diagnose "arg1 'bitwise' arg2 'eq' arg3". + if (BinaryOperator::isBitwiseOp(Opc)) + DiagnoseBitwisePrecedence(Self, Opc, OpLoc, LHSExpr, RHSExpr); + + // Diagnose "arg1 & arg2 | arg3" + if ((Opc == BO_Or || Opc == BO_Xor) && + !OpLoc.isMacroID()/* Don't warn in macros. */) { + DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, LHSExpr); + DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, RHSExpr); + } + + // Warn about arg1 || arg2 && arg3, as GCC 4.3+ does. + // We don't warn for 'assert(a || b && "bad")' since this is safe. + if (Opc == BO_LOr && !OpLoc.isMacroID()/* Don't warn in macros. */) { + DiagnoseLogicalAndInLogicalOrLHS(Self, OpLoc, LHSExpr, RHSExpr); + DiagnoseLogicalAndInLogicalOrRHS(Self, OpLoc, LHSExpr, RHSExpr); + } + + if ((Opc == BO_Shl && LHSExpr->getType()->isIntegralType(Self.getASTContext())) + || Opc == BO_Shr) { + StringRef Shift = BinaryOperator::getOpcodeStr(Opc); + DiagnoseAdditionInShift(Self, OpLoc, LHSExpr, Shift); + DiagnoseAdditionInShift(Self, OpLoc, RHSExpr, Shift); + } + + // Warn on overloaded shift operators and comparisons, such as: + // cout << 5 == 4; + if (BinaryOperator::isComparisonOp(Opc)) + DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr); +} + +ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, + Expr *LHSExpr, Expr *RHSExpr) { + BinaryOperatorKind Opc = ConvertTokenKindToBinaryOpcode(Kind); + assert(LHSExpr && "ActOnBinOp(): missing left expression"); + assert(RHSExpr && "ActOnBinOp(): missing right expression"); + + // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" + DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); + + return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); +} + +void Sema::LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, + UnresolvedSetImpl &Functions) { + OverloadedOperatorKind OverOp = BinaryOperator::getOverloadedOperator(Opc); + if (OverOp != OO_None && OverOp != OO_Equal) + LookupOverloadedOperatorName(OverOp, S, Functions); + + // In C++20 onwards, we may have a second operator to look up. + if (getLangOpts().CPlusPlus20) { + if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp)) + LookupOverloadedOperatorName(ExtraOp, S, Functions); + } +} + +/// Build an overloaded binary operator expression in the given scope. +static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS) { + switch (Opc) { + case BO_Assign: + // In the non-overloaded case, we warn about self-assignment (x = x) for + // both simple assignment and certain compound assignments where algebra + // tells us the operation yields a constant result. When the operator is + // overloaded, we can't do the latter because we don't want to assume that + // those algebraic identities still apply; for example, a path-building + // library might use operator/= to append paths. But it's still reasonable + // to assume that simple assignment is just moving/copying values around + // and so self-assignment is likely a bug. + DiagnoseSelfAssignment(S, LHS, RHS, OpLoc, false); + [[fallthrough]]; + case BO_DivAssign: + case BO_RemAssign: + case BO_SubAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + CheckIdentityFieldAssignment(LHS, RHS, OpLoc, S); + break; + default: + break; + } + + // Find all of the overloaded operators visible from this point. + UnresolvedSet<16> Functions; + S.LookupBinOp(Sc, OpLoc, Opc, Functions); + + // Build the (potentially-overloaded, potentially-dependent) + // binary operation. + return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); +} + +ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHSExpr, Expr *RHSExpr) { + ExprResult LHS, RHS; + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); + LHSExpr = LHS.get(); + RHSExpr = RHS.get(); + + // We want to end up calling one of SemaPseudoObject::checkAssignment + // (if the LHS is a pseudo-object), BuildOverloadedBinOp (if + // both expressions are overloadable or either is type-dependent), + // or CreateBuiltinBinOp (in any other case). We also want to get + // any placeholder types out of the way. + + // Handle pseudo-objects in the LHS. + if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { + // Assignments with a pseudo-object l-value need special analysis. + if (pty->getKind() == BuiltinType::PseudoObject && + BinaryOperator::isAssignmentOp(Opc)) + return PseudoObject().checkAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); + + // Don't resolve overloads if the other type is overloadable. + if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload) { + // We can't actually test that if we still have a placeholder, + // though. Fortunately, none of the exceptions we see in that + // code below are valid when the LHS is an overload set. Note + // that an overload set can be dependently-typed, but it never + // instantiates to having an overloadable type. + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (resolvedRHS.isInvalid()) return ExprError(); + RHSExpr = resolvedRHS.get(); + + if (RHSExpr->isTypeDependent() || + RHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + } + + // If we're instantiating "a.x < b" or "A::x < b" and 'x' names a function + // template, diagnose the missing 'template' keyword instead of diagnosing + // an invalid use of a bound member function. + // + // Note that "A::x < b" might be valid if 'b' has an overloadable type due + // to C++1z [over.over]/1.4, but we already checked for that case above. + if (Opc == BO_LT && inTemplateInstantiation() && + (pty->getKind() == BuiltinType::BoundMember || + pty->getKind() == BuiltinType::Overload)) { + auto *OE = dyn_cast(LHSExpr); + if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() && + llvm::any_of(OE->decls(), [](NamedDecl *ND) { + return isa(ND); + })) { + Diag(OE->getQualifier() ? OE->getQualifierLoc().getBeginLoc() + : OE->getNameLoc(), + diag::err_template_kw_missing) + << OE->getName().getAsString() << ""; + return ExprError(); + } + } + + ExprResult LHS = CheckPlaceholderExpr(LHSExpr); + if (LHS.isInvalid()) return ExprError(); + LHSExpr = LHS.get(); + } + + // Handle pseudo-objects in the RHS. + if (const BuiltinType *pty = RHSExpr->getType()->getAsPlaceholderType()) { + // An overload in the RHS can potentially be resolved by the type + // being assigned to. + if (Opc == BO_Assign && pty->getKind() == BuiltinType::Overload) { + if (getLangOpts().CPlusPlus && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || + LHSExpr->getType()->isOverloadableType())) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + } + + // Don't resolve overloads if the other type is overloadable. + if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload && + LHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (!resolvedRHS.isUsable()) return ExprError(); + RHSExpr = resolvedRHS.get(); + } + + if (getLangOpts().CPlusPlus) { + // Otherwise, build an overloaded op if either expression is type-dependent + // or has an overloadable type. + if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || + LHSExpr->getType()->isOverloadableType() || + RHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + } + + if (getLangOpts().RecoveryAST && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && + "Should only occur in error-recovery path."); + if (BinaryOperator::isCompoundAssignmentOp(Opc)) + // C [6.15.16] p3: + // An assignment expression has the value of the left operand after the + // assignment, but is not an lvalue. + return CompoundAssignOperator::Create( + Context, LHSExpr, RHSExpr, Opc, + LHSExpr->getType().getUnqualifiedType(), VK_PRValue, OK_Ordinary, + OpLoc, CurFPFeatureOverrides()); + QualType ResultType; + switch (Opc) { + case BO_Assign: + ResultType = LHSExpr->getType().getUnqualifiedType(); + break; + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + case BO_LAnd: + case BO_LOr: + // These operators have a fixed result type regardless of operands. + ResultType = Context.IntTy; + break; + case BO_Comma: + ResultType = RHSExpr->getType(); + break; + default: + ResultType = Context.DependentTy; + break; + } + return BinaryOperator::Create(Context, LHSExpr, RHSExpr, Opc, ResultType, + VK_PRValue, OK_Ordinary, OpLoc, + CurFPFeatureOverrides()); + } + + // Build a built-in binary operation. + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); +} + +static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { + if (T.isNull() || T->isDependentType()) + return false; + + if (!Ctx.isPromotableIntegerType(T)) + return true; + + return Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); +} + +ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, + UnaryOperatorKind Opc, Expr *InputExpr, + bool IsAfterAmp) { + ExprResult Input = InputExpr; + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + QualType resultType; + bool CanOverflow = false; + + bool ConvertHalfVec = false; + if (getLangOpts().OpenCL) { + QualType Ty = InputExpr->getType(); + // The only legal unary operation for atomics is '&'. + if ((Opc != UO_AddrOf && Ty->isAtomicType()) || + // OpenCL special types - image, sampler, pipe, and blocks are to be used + // only with a builtin functions and therefore should be disallowed here. + (Ty->isImageType() || Ty->isSamplerT() || Ty->isPipeType() + || Ty->isBlockPointerType())) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << InputExpr->getType() + << Input.get()->getSourceRange()); + } + } + + if (getLangOpts().HLSL && OpLoc.isValid()) { + if (Opc == UO_AddrOf) + return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 0); + if (Opc == UO_Deref) + return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1); + } + + if (InputExpr->isTypeDependent() && + InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) { + resultType = Context.DependentTy; + } else { + switch (Opc) { + case UO_PreInc: + case UO_PreDec: + case UO_PostInc: + case UO_PostDec: + resultType = + CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, + Opc == UO_PreInc || Opc == UO_PostInc, + Opc == UO_PreInc || Opc == UO_PreDec); + CanOverflow = isOverflowingIntegerType(Context, resultType); + break; + case UO_AddrOf: + resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); + RecordModifiableNonNullParam(*this, InputExpr); + break; + case UO_Deref: { + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = + CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); + break; + } + case UO_Plus: + case UO_Minus: + CanOverflow = Opc == UO_Minus && + isOverflowingIntegerType(Context, Input.get()->getType()); + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + // Unary plus and minus require promoting an operand of half vector to a + // float vector and truncating the result back to a half vector. For now, + // we do this only when HalfArgsAndReturns is set (that is, when the + // target is arm or arm64). + ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); + + // If the operand is a half vector, promote it to a float vector. + if (ConvertHalfVec) + Input = convertVector(Input.get(), Context.FloatTy, *this); + resultType = Input.get()->getType(); + if (resultType->isArithmeticType()) // C99 6.5.3.3p1 + break; + else if (resultType->isVectorType() && + // The z vector extensions don't allow + or - with bool vectors. + (!Context.getLangOpts().ZVector || + resultType->castAs()->getVectorKind() != + VectorKind::AltiVecBool)) + break; + else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - + break; + else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 + Opc == UO_Plus && resultType->isPointerType()) + break; + + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + + case UO_Not: // bitwise complement + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. + if (resultType->isComplexType() || resultType->isComplexIntegerType()) + // C99 does not support '~' for complex conjugation. + Diag(OpLoc, diag::ext_integer_complement_complex) + << resultType << Input.get()->getSourceRange(); + else if (resultType->hasIntegerRepresentation()) + break; + else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { + // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate + // on vector float types. + QualType T = resultType->castAs()->getElementType(); + if (!T->isIntegerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + break; + + case UO_LNot: // logical negation + // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + + // Though we still have to promote half FP to float... + if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { + Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) + .get(); + resultType = Context.FloatTy; + } + + // WebAsembly tables can't be used in unary expressions. + if (resultType->isPointerType() && + resultType->getPointeeType().isWebAssemblyReferenceType()) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + + if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { + // C99 6.5.3.3p1: ok, fallthrough; + if (Context.getLangOpts().CPlusPlus) { + // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: + // operand contextually converted to bool. + Input = ImpCastExprToType(Input.get(), Context.BoolTy, + ScalarTypeToBooleanCastKind(resultType)); + } else if (Context.getLangOpts().OpenCL && + Context.getLangOpts().OpenCLVersion < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on scalar float types. + if (!resultType->isIntegerType() && !resultType->isPointerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + } else if (resultType->isExtVectorType()) { + if (Context.getLangOpts().OpenCL && + Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on vector float types. + QualType T = resultType->castAs()->getElementType(); + if (!T->isIntegerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else if (Context.getLangOpts().CPlusPlus && + resultType->isVectorType()) { + const VectorType *VTy = resultType->castAs(); + if (VTy->getVectorKind() != VectorKind::Generic) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + + // LNot always has type int. C99 6.5.3.3p5. + // In C++, it's bool. C++ 5.3.1p8 + resultType = Context.getLogicalOperationType(); + break; + case UO_Real: + case UO_Imag: + resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); + // _Real maps ordinary l-values into ordinary l-values. _Imag maps + // ordinary complex l-values to ordinary l-values and all other values to + // r-values. + if (Input.isInvalid()) + return ExprError(); + if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { + if (Input.get()->isGLValue() && + Input.get()->getObjectKind() == OK_Ordinary) + VK = Input.get()->getValueKind(); + } else if (!getLangOpts().CPlusPlus) { + // In C, a volatile scalar is read by __imag. In C++, it is not. + Input = DefaultLvalueConversion(Input.get()); + } + break; + case UO_Extension: + resultType = Input.get()->getType(); + VK = Input.get()->getValueKind(); + OK = Input.get()->getObjectKind(); + break; + case UO_Coawait: + // It's unnecessary to represent the pass-through operator co_await in the + // AST; just return the input expression instead. + assert(!Input.get()->getType()->isDependentType() && + "the co_await expression must be non-dependant before " + "building operator co_await"); + return Input; + } + } + if (resultType.isNull() || Input.isInvalid()) + return ExprError(); + + // Check for array bounds violations in the operand of the UnaryOperator, + // except for the '*' and '&' operators that have to be handled specially + // by CheckArrayAccess (as there are special cases like &array[arraysize] + // that are explicitly defined as valid by the standard). + if (Opc != UO_AddrOf && Opc != UO_Deref) + CheckArrayAccess(Input.get()); + + auto *UO = + UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK, + OpLoc, CanOverflow, CurFPFeatureOverrides()); + + if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && + !isa(UO->getType().getDesugaredType(Context)) && + !isUnevaluatedContext()) + ExprEvalContexts.back().PossibleDerefs.insert(UO); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + FlexibleArrayMemberUtils FlexUtils(*this); + if (Opc == UO_AddrOf) { + // when taking the address of an lvalue with a flexible array member, + // return a promoted pointer to it + if (auto *RD = FlexUtils.GetFlexibleRecord(Input.get()->getType())) { + // Skipping the null check for the struct base because 'address of' is + // never null. + return PromoteBoundsSafetyPointerToFlexibleArrayMember( + *this, RD, UO, /*NullCheck*/ false); + } + } else if (Opc == UO_Deref) { + // when dereferencing a flexible array member struct, do a bounds check + // to verify that the number of elements fits the size of the pointer + auto PtrTy = Input.get()->getType()->getAs(); + if (PtrTy && PtrTy->getPointerAttributes().hasUpperBound()) { + if (auto *RD = FlexUtils.GetFlexibleRecord(PtrTy->getPointeeType())) { + // Check if this has been promoted from a __single pointer to flexible + // array member struct. If so, we skip the check since being __single + // should mean the count value is already valid. + bool SkipCheck = false; + auto InnerTy = Input.get()->IgnoreImpCasts()->getType(); + if (InnerTy->isSinglePointerType() && + !InnerTy->isBoundsAttributedType()) { + auto *InnerRD = + FlexUtils.GetFlexibleRecord(InnerTy->getPointeeType()); + if (InnerRD == RD) + SkipCheck = true; + } + + if (!SkipCheck) { + ExprResult Ckd = BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + *this, UO->getBeginLoc(), + BoundsCheckKind::FlexibleArrayCountDeref, Input.get()); + if (!Ckd.get()) + return ExprError(); + UO->setSubExpr(Ckd.get()); + } + } + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + // Convert the result back to a half vector. + if (ConvertHalfVec) + return convertVector(UO, Context.HalfTy, *this); + return UO; +} + +bool Sema::isQualifiedMemberAccess(Expr *E) { + if (DeclRefExpr *DRE = dyn_cast(E)) { + if (!DRE->getQualifier()) + return false; + + ValueDecl *VD = DRE->getDecl(); + if (!VD->isCXXClassMember()) + return false; + + if (isa(VD) || isa(VD)) + return true; + if (CXXMethodDecl *Method = dyn_cast(VD)) + return Method->isImplicitObjectMemberFunction(); + + return false; + } + + if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { + if (!ULE->getQualifier()) + return false; + + for (NamedDecl *D : ULE->decls()) { + if (CXXMethodDecl *Method = dyn_cast(D)) { + if (Method->isImplicitObjectMemberFunction()) + return true; + } else { + // Overload set does not contain methods. + break; + } + } + + return false; + } + + return false; +} + +ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc, + UnaryOperatorKind Opc, Expr *Input, + bool IsAfterAmp) { + // First things first: handle placeholders so that the + // overloaded-operator check considers the right type. + if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { + // Increment and decrement of pseudo-object references. + if (pty->getKind() == BuiltinType::PseudoObject && + UnaryOperator::isIncrementDecrementOp(Opc)) + return PseudoObject().checkIncDec(S, OpLoc, Opc, Input); + + // extension is always a builtin operator. + if (Opc == UO_Extension) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + + // & gets special logic for several kinds of placeholder. + // The builtin code knows what to do. + if (Opc == UO_AddrOf && + (pty->getKind() == BuiltinType::Overload || + pty->getKind() == BuiltinType::UnknownAny || + pty->getKind() == BuiltinType::BoundMember)) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + + // Anything else needs to be handled now. + ExprResult Result = CheckPlaceholderExpr(Input); + if (Result.isInvalid()) return ExprError(); + Input = Result.get(); + } + + if (getLangOpts().CPlusPlus && Input->getType()->isOverloadableType() && + UnaryOperator::getOverloadedOperator(Opc) != OO_None && + !(Opc == UO_AddrOf && isQualifiedMemberAccess(Input))) { + // Find all of the overloaded operators visible from this point. + UnresolvedSet<16> Functions; + OverloadedOperatorKind OverOp = UnaryOperator::getOverloadedOperator(Opc); + if (S && OverOp != OO_None) + LookupOverloadedOperatorName(OverOp, S, Functions); + + return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input); + } + + return CreateBuiltinUnaryOp(OpLoc, Opc, Input, IsAfterAmp); +} + +ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, + Expr *Input, bool IsAfterAmp) { + return BuildUnaryOp(S, OpLoc, ConvertTokenKindToUnaryOpcode(Op), Input, + IsAfterAmp); +} + +ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, + LabelDecl *TheDecl) { + TheDecl->markUsed(Context); + // Create the AST node. The address of a label always has type 'void*'. + auto *Res = new (Context) AddrLabelExpr( + OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); + + if (getCurFunction()) + getCurFunction()->AddrLabels.push_back(Res); + + return Res; +} + +void Sema::ActOnStartStmtExpr() { + PushExpressionEvaluationContext(ExprEvalContexts.back().Context); + // Make sure we diagnose jumping into a statement expression. + setFunctionHasBranchProtectedScope(); +} + +void Sema::ActOnStmtExprError() { + // Note that function is also called by TreeTransform when leaving a + // StmtExpr scope without rebuilding anything. + + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); +} + +ExprResult Sema::ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt, + SourceLocation RPLoc) { + return BuildStmtExpr(LPLoc, SubStmt, RPLoc, getTemplateDepth(S)); +} + +ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, + SourceLocation RPLoc, unsigned TemplateDepth) { + assert(SubStmt && isa(SubStmt) && "Invalid action invocation!"); + CompoundStmt *Compound = cast(SubStmt); + + if (hasAnyUnrecoverableErrorsInThisFunction()) + DiscardCleanupsInEvaluationContext(); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within StmtExpr not correctly bound!"); + PopExpressionEvaluationContext(); + + // FIXME: there are a variety of strange constraints to enforce here, for + // example, it is not possible to goto into a stmt expression apparently. + // More semantic analysis is needed. + + // If there are sub-stmts in the compound stmt, take the type of the last one + // as the type of the stmtexpr. + QualType Ty = Context.VoidTy; + bool StmtExprMayBindToTemp = false; + if (!Compound->body_empty()) { + // For GCC compatibility we get the last Stmt excluding trailing NullStmts. + if (const auto *LastStmt = + dyn_cast(Compound->getStmtExprResult())) { + if (const Expr *Value = LastStmt->getExprStmt()) { + StmtExprMayBindToTemp = true; + Ty = Value->getType(); + } + } + } + + // FIXME: Check that expression type is complete/non-abstract; statement + // expressions are not lvalues. + Expr *ResStmtExpr = + new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth); + if (StmtExprMayBindToTemp) + return MaybeBindToTemporary(ResStmtExpr); + return ResStmtExpr; +} + +ExprResult Sema::ActOnStmtExprResult(ExprResult ER) { + if (ER.isInvalid()) + return ExprError(); + + // Do function/array conversion on the last expression, but not + // lvalue-to-rvalue. However, initialize an unqualified type. + ER = DefaultFunctionArrayConversion(ER.get()); + if (ER.isInvalid()) + return ExprError(); + Expr *E = ER.get(); + + if (E->isTypeDependent()) + return E; + + // In ARC, if the final expression ends in a consume, splice + // the consume out and bind it later. In the alternate case + // (when dealing with a retainable type), the result + // initialization will create a produce. In both cases the + // result will be +1, and we'll need to balance that out with + // a bind. + auto *Cast = dyn_cast(E); + if (Cast && Cast->getCastKind() == CK_ARCConsumeObject) + return Cast->getSubExpr(); + + // FIXME: Provide a better location for the initialization. + return PerformCopyInitialization( + InitializedEntity::InitializeStmtExprResult( + E->getBeginLoc(), E->getType().getUnqualifiedType()), + SourceLocation(), E); +} + +ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, + TypeSourceInfo *TInfo, + ArrayRef Components, + SourceLocation RParenLoc) { + QualType ArgTy = TInfo->getType(); + bool Dependent = ArgTy->isDependentType(); + SourceRange TypeRange = TInfo->getTypeLoc().getLocalSourceRange(); + + // We must have at least one component that refers to the type, and the first + // one is known to be a field designator. Verify that the ArgTy represents + // a struct/union/class. + if (!Dependent && !ArgTy->isRecordType()) + return ExprError(Diag(BuiltinLoc, diag::err_offsetof_record_type) + << ArgTy << TypeRange); + + // Type must be complete per C99 7.17p3 because a declaring a variable + // with an incomplete type would be ill-formed. + if (!Dependent + && RequireCompleteType(BuiltinLoc, ArgTy, + diag::err_offsetof_incomplete_type, TypeRange)) + return ExprError(); + + bool DidWarnAboutNonPOD = false; + QualType CurrentType = ArgTy; + SmallVector Comps; + SmallVector Exprs; + for (const OffsetOfComponent &OC : Components) { + if (OC.isBrackets) { + // Offset of an array sub-field. TODO: Should we allow vector elements? + if (!CurrentType->isDependentType()) { + const ArrayType *AT = Context.getAsArrayType(CurrentType); + if(!AT) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) + << CurrentType); + CurrentType = AT->getElementType(); + } else + CurrentType = Context.DependentTy; + + ExprResult IdxRval = DefaultLvalueConversion(static_cast(OC.U.E)); + if (IdxRval.isInvalid()) + return ExprError(); + Expr *Idx = IdxRval.get(); + + // The expression must be an integral expression. + // FIXME: An integral constant expression? + if (!Idx->isTypeDependent() && !Idx->isValueDependent() && + !Idx->getType()->isIntegerType()) + return ExprError( + Diag(Idx->getBeginLoc(), diag::err_typecheck_subscript_not_integer) + << Idx->getSourceRange()); + + // Record this array index. + Comps.push_back(OffsetOfNode(OC.LocStart, Exprs.size(), OC.LocEnd)); + Exprs.push_back(Idx); + continue; + } + + // Offset of a field. + if (CurrentType->isDependentType()) { + // We have the offset of a field, but we can't look into the dependent + // type. Just record the identifier of the field. + Comps.push_back(OffsetOfNode(OC.LocStart, OC.U.IdentInfo, OC.LocEnd)); + CurrentType = Context.DependentTy; + continue; + } + + // We need to have a complete type to look into. + if (RequireCompleteType(OC.LocStart, CurrentType, + diag::err_offsetof_incomplete_type)) + return ExprError(); + + // Look for the designated field. + const RecordType *RC = CurrentType->getAs(); + if (!RC) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) + << CurrentType); + RecordDecl *RD = RC->getDecl(); + + // C++ [lib.support.types]p5: + // The macro offsetof accepts a restricted set of type arguments in this + // International Standard. type shall be a POD structure or a POD union + // (clause 9). + // C++11 [support.types]p4: + // If type is not a standard-layout class (Clause 9), the results are + // undefined. + if (CXXRecordDecl *CRD = dyn_cast(RD)) { + bool IsSafe = LangOpts.CPlusPlus11? CRD->isStandardLayout() : CRD->isPOD(); + unsigned DiagID = + LangOpts.CPlusPlus11? diag::ext_offsetof_non_standardlayout_type + : diag::ext_offsetof_non_pod_type; + + if (!IsSafe && !DidWarnAboutNonPOD && !isUnevaluatedContext()) { + Diag(BuiltinLoc, DiagID) + << SourceRange(Components[0].LocStart, OC.LocEnd) << CurrentType; + DidWarnAboutNonPOD = true; + } + } + + // Look for the field. + LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); + LookupQualifiedName(R, RD); + FieldDecl *MemberDecl = R.getAsSingle(); + IndirectFieldDecl *IndirectMemberDecl = nullptr; + if (!MemberDecl) { + if ((IndirectMemberDecl = R.getAsSingle())) + MemberDecl = IndirectMemberDecl->getAnonField(); + } + + if (!MemberDecl) { + // Lookup could be ambiguous when looking up a placeholder variable + // __builtin_offsetof(S, _). + // In that case we would already have emitted a diagnostic + if (!R.isAmbiguous()) + Diag(BuiltinLoc, diag::err_no_member) + << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd); + return ExprError(); + } + + // C99 7.17p3: + // (If the specified member is a bit-field, the behavior is undefined.) + // + // We diagnose this as an error. + if (MemberDecl->isBitField()) { + Diag(OC.LocEnd, diag::err_offsetof_bitfield) + << MemberDecl->getDeclName() + << SourceRange(BuiltinLoc, RParenLoc); + Diag(MemberDecl->getLocation(), diag::note_bitfield_decl); + return ExprError(); + } + + RecordDecl *Parent = MemberDecl->getParent(); + if (IndirectMemberDecl) + Parent = cast(IndirectMemberDecl->getDeclContext()); + + // If the member was found in a base class, introduce OffsetOfNodes for + // the base class indirections. + CXXBasePaths Paths; + if (IsDerivedFrom(OC.LocStart, CurrentType, Context.getTypeDeclType(Parent), + Paths)) { + if (Paths.getDetectedVirtual()) { + Diag(OC.LocEnd, diag::err_offsetof_field_of_virtual_base) + << MemberDecl->getDeclName() + << SourceRange(BuiltinLoc, RParenLoc); + return ExprError(); + } + + CXXBasePath &Path = Paths.front(); + for (const CXXBasePathElement &B : Path) + Comps.push_back(OffsetOfNode(B.Base)); + } + + if (IndirectMemberDecl) { + for (auto *FI : IndirectMemberDecl->chain()) { + assert(isa(FI)); + Comps.push_back(OffsetOfNode(OC.LocStart, + cast(FI), OC.LocEnd)); + } + } else + Comps.push_back(OffsetOfNode(OC.LocStart, MemberDecl, OC.LocEnd)); + + CurrentType = MemberDecl->getType().getNonReferenceType(); + } + + return OffsetOfExpr::Create(Context, Context.getSizeType(), BuiltinLoc, TInfo, + Comps, Exprs, RParenLoc); +} + +ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, + SourceLocation BuiltinLoc, + SourceLocation TypeLoc, + ParsedType ParsedArgTy, + ArrayRef Components, + SourceLocation RParenLoc) { + + TypeSourceInfo *ArgTInfo; + QualType ArgTy = GetTypeFromParser(ParsedArgTy, &ArgTInfo); + if (ArgTy.isNull()) + return ExprError(); + + if (!ArgTInfo) + ArgTInfo = Context.getTrivialTypeSourceInfo(ArgTy, TypeLoc); + + return BuildBuiltinOffsetOf(BuiltinLoc, ArgTInfo, Components, RParenLoc); +} + + +ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, + Expr *CondExpr, + Expr *LHSExpr, Expr *RHSExpr, + SourceLocation RPLoc) { + assert((CondExpr && LHSExpr && RHSExpr) && "Missing type argument(s)"); + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + QualType resType; + bool CondIsTrue = false; + if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { + resType = Context.DependentTy; + } else { + // The conditional expression is required to be a constant expression. + llvm::APSInt condEval(32); + ExprResult CondICE = VerifyIntegerConstantExpression( + CondExpr, &condEval, diag::err_typecheck_choose_expr_requires_constant); + if (CondICE.isInvalid()) + return ExprError(); + CondExpr = CondICE.get(); + CondIsTrue = condEval.getZExtValue(); + + // If the condition is > zero, then the AST type is the same as the LHSExpr. + Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr; + + resType = ActiveExpr->getType(); + VK = ActiveExpr->getValueKind(); + OK = ActiveExpr->getObjectKind(); + } + + return new (Context) ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, + resType, VK, OK, RPLoc, CondIsTrue); +} + +//===----------------------------------------------------------------------===// +// Clang Extensions. +//===----------------------------------------------------------------------===// + +void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { + BlockDecl *Block = BlockDecl::Create(Context, CurContext, CaretLoc); + + if (LangOpts.CPlusPlus) { + MangleNumberingContext *MCtx; + Decl *ManglingContextDecl; + std::tie(MCtx, ManglingContextDecl) = + getCurrentMangleNumberContext(Block->getDeclContext()); + if (MCtx) { + unsigned ManglingNumber = MCtx->getManglingNumber(Block); + Block->setBlockMangling(ManglingNumber, ManglingContextDecl); + } + } + + PushBlockScope(CurScope, Block); + CurContext->addDecl(Block); + if (CurScope) + PushDeclContext(CurScope, Block); + else + CurContext = Block; + + getCurBlock()->HasImplicitReturnType = true; + + // Enter a new evaluation context to insulate the block from any + // cleanups from the enclosing full-expression. + PushExpressionEvaluationContext( + ExpressionEvaluationContext::PotentiallyEvaluated); +} + +void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, + Scope *CurScope) { + assert(ParamInfo.getIdentifier() == nullptr && + "block-id should have no identifier!"); + assert(ParamInfo.getContext() == DeclaratorContext::BlockLiteral); + BlockScopeInfo *CurBlock = getCurBlock(); + + TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo); + QualType T = Sig->getType(); + + // FIXME: We should allow unexpanded parameter packs here, but that would, + // in turn, make the block expression contain unexpanded parameter packs. + if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) { + // Drop the parameters. + FunctionProtoType::ExtProtoInfo EPI; + EPI.HasTrailingReturn = false; + EPI.TypeQuals.addConst(); + T = Context.getFunctionType(Context.DependentTy, std::nullopt, EPI); + Sig = Context.getTrivialTypeSourceInfo(T); + } + + // GetTypeForDeclarator always produces a function type for a block + // literal signature. Furthermore, it is always a FunctionProtoType + // unless the function was written with a typedef. + assert(T->isFunctionType() && + "GetTypeForDeclarator made a non-function block signature"); + + // Look for an explicit signature in that function type. + FunctionProtoTypeLoc ExplicitSignature; + + if ((ExplicitSignature = Sig->getTypeLoc() + .getAsAdjusted())) { + + // Check whether that explicit signature was synthesized by + // GetTypeForDeclarator. If so, don't save that as part of the + // written signature. + if (ExplicitSignature.getLocalRangeBegin() == + ExplicitSignature.getLocalRangeEnd()) { + // This would be much cheaper if we stored TypeLocs instead of + // TypeSourceInfos. + TypeLoc Result = ExplicitSignature.getReturnLoc(); + unsigned Size = Result.getFullDataSize(); + Sig = Context.CreateTypeSourceInfo(Result.getType(), Size); + Sig->getTypeLoc().initializeFullCopy(Result, Size); + + ExplicitSignature = FunctionProtoTypeLoc(); + } + } + + CurBlock->TheDecl->setSignatureAsWritten(Sig); + CurBlock->FunctionType = T; + + const auto *Fn = T->castAs(); + QualType RetTy = Fn->getReturnType(); + bool isVariadic = + (isa(Fn) && cast(Fn)->isVariadic()); + + CurBlock->TheDecl->setIsVariadic(isVariadic); + + // Context.DependentTy is used as a placeholder for a missing block + // return type. TODO: what should we do with declarators like: + // ^ * { ... } + // If the answer is "apply template argument deduction".... + if (RetTy != Context.DependentTy) { + CurBlock->ReturnType = RetTy; + CurBlock->TheDecl->setBlockMissingReturnType(false); + CurBlock->HasImplicitReturnType = false; + } + + // Push block parameters from the declarator if we had them. + SmallVector Params; + if (ExplicitSignature) { + for (unsigned I = 0, E = ExplicitSignature.getNumParams(); I != E; ++I) { + ParmVarDecl *Param = ExplicitSignature.getParam(I); + if (Param->getIdentifier() == nullptr && !Param->isImplicit() && + !Param->isInvalidDecl() && !getLangOpts().CPlusPlus) { + // Diagnose this as an extension in C17 and earlier. + if (!getLangOpts().C23) + Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23); + } + Params.push_back(Param); + } + + // Fake up parameter variables if we have a typedef, like + // ^ fntype { ... } + } else if (const FunctionProtoType *Fn = T->getAs()) { + for (const auto &I : Fn->param_types()) { + ParmVarDecl *Param = BuildParmVarDeclForTypedef( + CurBlock->TheDecl, ParamInfo.getBeginLoc(), I); + Params.push_back(Param); + } + } + + // Set the parameters on the block decl. + if (!Params.empty()) { + CurBlock->TheDecl->setParams(Params); + CheckParmsForFunctionDef(CurBlock->TheDecl->parameters(), + /*CheckParameterNames=*/false); + } + + // Finally we can process decl attributes. + ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo); + + // Put the parameter variables in scope. + for (auto *AI : CurBlock->TheDecl->parameters()) { + AI->setOwningFunction(CurBlock->TheDecl); + + // If this has an identifier, add it to the scope stack. + if (AI->getIdentifier()) { + CheckShadow(CurBlock->TheScope, AI); + + PushOnScopeChains(AI, CurBlock->TheScope); + } + + if (AI->isInvalidDecl()) + CurBlock->TheDecl->setInvalidDecl(); + } +} + +void Sema::ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) { + // Leave the expression-evaluation context. + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + // Pop off CurBlock, handle nested blocks. + PopDeclContext(); + PopFunctionScopeInfo(); +} + +ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, + Stmt *Body, Scope *CurScope) { + // If blocks are disabled, emit an error. + if (!LangOpts.Blocks) + Diag(CaretLoc, diag::err_blocks_disable) << LangOpts.OpenCL; + + // Leave the expression-evaluation context. + if (hasAnyUnrecoverableErrorsInThisFunction()) + DiscardCleanupsInEvaluationContext(); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within block not correctly bound!"); + PopExpressionEvaluationContext(); + + BlockScopeInfo *BSI = cast(FunctionScopes.back()); + BlockDecl *BD = BSI->TheDecl; + + maybeAddDeclWithEffects(BD); + + if (BSI->HasImplicitReturnType) + deduceClosureReturnType(*BSI); + + QualType RetTy = Context.VoidTy; + if (!BSI->ReturnType.isNull()) + RetTy = BSI->ReturnType; + + bool NoReturn = BD->hasAttr(); + QualType BlockTy; + + // If the user wrote a function type in some form, try to use that. + if (!BSI->FunctionType.isNull()) { + const FunctionType *FTy = BSI->FunctionType->castAs(); + + FunctionType::ExtInfo Ext = FTy->getExtInfo(); + if (NoReturn && !Ext.getNoReturn()) Ext = Ext.withNoReturn(true); + + // Turn protoless block types into nullary block types. + if (isa(FTy)) { + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExtInfo = Ext; + BlockTy = Context.getFunctionType(RetTy, std::nullopt, EPI); + + // Otherwise, if we don't need to change anything about the function type, + // preserve its sugar structure. + } else if (FTy->getReturnType() == RetTy && + (!NoReturn || FTy->getNoReturnAttr())) { + BlockTy = BSI->FunctionType; + + // Otherwise, make the minimal modifications to the function type. + } else { + const FunctionProtoType *FPT = cast(FTy); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.TypeQuals = Qualifiers(); + EPI.ExtInfo = Ext; + BlockTy = Context.getFunctionType(RetTy, FPT->getParamTypes(), EPI); + } + + // If we don't have a function type, just build one from nothing. + } else { + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); + BlockTy = Context.getFunctionType(RetTy, std::nullopt, EPI); + } + + DiagnoseUnusedParameters(BD->parameters()); + BlockTy = Context.getBlockPointerType(BlockTy); + + // If needed, diagnose invalid gotos and switches in the block. + if (getCurFunction()->NeedsScopeChecking() && + !PP.isCodeCompletionEnabled()) + DiagnoseInvalidJumps(cast(Body)); + + BD->setBody(cast(Body)); + + if (Body && getCurFunction()->HasPotentialAvailabilityViolations) + DiagnoseUnguardedAvailabilityViolations(BD); + + // Try to apply the named return value optimization. We have to check again + // if we can do this, though, because blocks keep return statements around + // to deduce an implicit return type. + if (getLangOpts().CPlusPlus && RetTy->isRecordType() && + !BD->isDependentContext()) + computeNRVO(Body, BSI); + + if (RetTy.hasNonTrivialToPrimitiveDestructCUnion() || + RetTy.hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn, + NTCUK_Destruct|NTCUK_Copy); + + PopDeclContext(); + + // Set the captured variables on the block. + SmallVector Captures; + for (Capture &Cap : BSI->Captures) { + if (Cap.isInvalid() || Cap.isThisCapture()) + continue; + // Cap.getVariable() is always a VarDecl because + // blocks cannot capture structured bindings or other ValueDecl kinds. + auto *Var = cast(Cap.getVariable()); + Expr *CopyExpr = nullptr; + if (getLangOpts().CPlusPlus && Cap.isCopyCapture()) { + if (const RecordType *Record = + Cap.getCaptureType()->getAs()) { + // The capture logic needs the destructor, so make sure we mark it. + // Usually this is unnecessary because most local variables have + // their destructors marked at declaration time, but parameters are + // an exception because it's technically only the call site that + // actually requires the destructor. + if (isa(Var)) + FinalizeVarWithDestructor(Var, Record); + + // Enter a separate potentially-evaluated context while building block + // initializers to isolate their cleanups from those of the block + // itself. + // FIXME: Is this appropriate even when the block itself occurs in an + // unevaluated operand? + EnterExpressionEvaluationContext EvalContext( + *this, ExpressionEvaluationContext::PotentiallyEvaluated); + + SourceLocation Loc = Cap.getLocation(); + + ExprResult Result = BuildDeclarationNameExpr( + CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var); + + // According to the blocks spec, the capture of a variable from + // the stack requires a const copy constructor. This is not true + // of the copy/move done to move a __block variable to the heap. + if (!Result.isInvalid() && + !Result.get()->getType().isConstQualified()) { + Result = ImpCastExprToType(Result.get(), + Result.get()->getType().withConst(), + CK_NoOp, VK_LValue); + } + + if (!Result.isInvalid()) { + Result = PerformCopyInitialization( + InitializedEntity::InitializeBlock(Var->getLocation(), + Cap.getCaptureType()), + Loc, Result.get()); + } + + // Build a full-expression copy expression if initialization + // succeeded and used a non-trivial constructor. Recover from + // errors by pretending that the copy isn't necessary. + if (!Result.isInvalid() && + !cast(Result.get())->getConstructor() + ->isTrivial()) { + Result = MaybeCreateExprWithCleanups(Result); + CopyExpr = Result.get(); + } + } + } + + BlockDecl::Capture NewCap(Var, Cap.isBlockCapture(), Cap.isNested(), + CopyExpr); + Captures.push_back(NewCap); + } + BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); + + // Pop the block scope now but keep it alive to the end of this function. + AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); + PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); + + BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy); + + // If the block isn't obviously global, i.e. it captures anything at + // all, then we need to do a few things in the surrounding context: + if (Result->getBlockDecl()->hasCaptures()) { + // First, this expression has a new cleanup object. + ExprCleanupObjects.push_back(Result->getBlockDecl()); + Cleanup.setExprNeedsCleanups(true); + + // It also gets a branch-protected scope if any of the captured + // variables needs destruction. + for (const auto &CI : Result->getBlockDecl()->captures()) { + const VarDecl *var = CI.getVariable(); + if (var->getType().isDestructedType() != QualType::DK_none) { + setFunctionHasBranchProtectedScope(); + break; + } + } + } + + if (getCurFunction()) + getCurFunction()->addBlock(BD); + + if (BD->isInvalidDecl()) + return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(), + {Result}, Result->getType()); + return Result; +} + +ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc, Expr *E, ParsedType Ty, + SourceLocation RPLoc) { + TypeSourceInfo *TInfo; + GetTypeFromParser(Ty, &TInfo); + return BuildVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); +} + +ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, + Expr *E, TypeSourceInfo *TInfo, + SourceLocation RPLoc) { + Expr *OrigExpr = E; + bool IsMS = false; + + // CUDA device code does not support varargs. + if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) { + if (const FunctionDecl *F = dyn_cast(CurContext)) { + CUDAFunctionTarget T = CUDA().IdentifyTarget(F); + if (T == CUDAFunctionTarget::Global || T == CUDAFunctionTarget::Device || + T == CUDAFunctionTarget::HostDevice) + return ExprError(Diag(E->getBeginLoc(), diag::err_va_arg_in_device)); + } + } + + // NVPTX does not support va_arg expression. + if (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && + Context.getTargetInfo().getTriple().isNVPTX()) + targetDiag(E->getBeginLoc(), diag::err_va_arg_in_device); + + // It might be a __builtin_ms_va_list. (But don't ever mark a va_arg() + // as Microsoft ABI on an actual Microsoft platform, where + // __builtin_ms_va_list and __builtin_va_list are the same.) + if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinMSVaList() && + Context.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList) { + QualType MSVaListType = Context.getBuiltinMSVaListType(); + if (Context.hasSameType(MSVaListType, E->getType())) { + if (CheckForModifiableLvalue(E, BuiltinLoc, *this)) + return ExprError(); + IsMS = true; + } + } + + // Get the va_list type + QualType VaListType = Context.getBuiltinVaListType(); + if (!IsMS) { + if (VaListType->isArrayType()) { + // Deal with implicit array decay; for example, on x86-64, + // va_list is an array, but it's supposed to decay to + // a pointer for va_arg. + VaListType = Context.getArrayDecayedType(VaListType); + // Make sure the input expression also decays appropriately. + ExprResult Result = UsualUnaryConversions(E); + if (Result.isInvalid()) + return ExprError(); + E = Result.get(); + } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) { + // If va_list is a record type and we are compiling in C++ mode, + // check the argument using reference binding. + InitializedEntity Entity = InitializedEntity::InitializeParameter( + Context, Context.getLValueReferenceType(VaListType), false); + ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E); + if (Init.isInvalid()) + return ExprError(); + E = Init.getAs(); + } else { + // Otherwise, the va_list argument must be an l-value because + // it is modified by va_arg. + if (!E->isTypeDependent() && + CheckForModifiableLvalue(E, BuiltinLoc, *this)) + return ExprError(); + } + } + + if (!IsMS && !E->isTypeDependent() && + !Context.hasSameType(VaListType, E->getType())) + return ExprError( + Diag(E->getBeginLoc(), + diag::err_first_argument_to_va_arg_not_of_type_va_list) + << OrigExpr->getType() << E->getSourceRange()); + + if (!TInfo->getType()->isDependentType()) { + if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), + diag::err_second_parameter_to_va_arg_incomplete, + TInfo->getTypeLoc())) + return ExprError(); + + if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), + diag::err_second_parameter_to_va_arg_abstract, + TInfo->getTypeLoc())) + return ExprError(); + + if (!TInfo->getType().isPODType(Context)) { + Diag(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType()->isObjCLifetimeType() + ? diag::warn_second_parameter_to_va_arg_ownership_qualified + : diag::warn_second_parameter_to_va_arg_not_pod) + << TInfo->getType() + << TInfo->getTypeLoc().getSourceRange(); + } + + // Check for va_arg where arguments of the given type will be promoted + // (i.e. this va_arg is guaranteed to have undefined behavior). + QualType PromoteType; + if (Context.isPromotableIntegerType(TInfo->getType())) { + PromoteType = Context.getPromotedIntegerType(TInfo->getType()); + // [cstdarg.syn]p1 defers the C++ behavior to what the C standard says, + // and C23 7.16.1.1p2 says, in part: + // If type is not compatible with the type of the actual next argument + // (as promoted according to the default argument promotions), the + // behavior is undefined, except for the following cases: + // - both types are pointers to qualified or unqualified versions of + // compatible types; + // - one type is compatible with a signed integer type, the other + // type is compatible with the corresponding unsigned integer type, + // and the value is representable in both types; + // - one type is pointer to qualified or unqualified void and the + // other is a pointer to a qualified or unqualified character type; + // - or, the type of the next argument is nullptr_t and type is a + // pointer type that has the same representation and alignment + // requirements as a pointer to a character type. + // Given that type compatibility is the primary requirement (ignoring + // qualifications), you would think we could call typesAreCompatible() + // directly to test this. However, in C++, that checks for *same type*, + // which causes false positives when passing an enumeration type to + // va_arg. Instead, get the underlying type of the enumeration and pass + // that. + QualType UnderlyingType = TInfo->getType(); + if (const auto *ET = UnderlyingType->getAs()) + UnderlyingType = ET->getDecl()->getIntegerType(); + if (Context.typesAreCompatible(PromoteType, UnderlyingType, + /*CompareUnqualified*/ true)) + PromoteType = QualType(); + + // If the types are still not compatible, we need to test whether the + // promoted type and the underlying type are the same except for + // signedness. Ask the AST for the correctly corresponding type and see + // if that's compatible. + if (!PromoteType.isNull() && !UnderlyingType->isBooleanType() && + PromoteType->isUnsignedIntegerType() != + UnderlyingType->isUnsignedIntegerType()) { + UnderlyingType = + UnderlyingType->isUnsignedIntegerType() + ? Context.getCorrespondingSignedType(UnderlyingType) + : Context.getCorrespondingUnsignedType(UnderlyingType); + if (Context.typesAreCompatible(PromoteType, UnderlyingType, + /*CompareUnqualified*/ true)) + PromoteType = QualType(); + } + } + if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) + PromoteType = Context.DoubleTy; + if (!PromoteType.isNull()) + DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, + PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) + << TInfo->getType() + << PromoteType + << TInfo->getTypeLoc().getSourceRange()); + } + + QualType T = TInfo->getType().getNonLValueExprType(Context); + return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS); +} + +ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { + // The type of __null will be int or long, depending on the size of + // pointers on the target. + QualType Ty; + unsigned pw = Context.getTargetInfo().getPointerWidth(LangAS::Default); + if (pw == Context.getTargetInfo().getIntWidth()) + Ty = Context.IntTy; + else if (pw == Context.getTargetInfo().getLongWidth()) + Ty = Context.LongTy; + else if (pw == Context.getTargetInfo().getLongLongWidth()) + Ty = Context.LongLongTy; + else { + llvm_unreachable("I don't know size of pointer!"); + } + + return new (Context) GNUNullExpr(Ty, TokenLoc); +} + +static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { + CXXRecordDecl *ImplDecl = nullptr; + + // Fetch the std::source_location::__impl decl. + if (NamespaceDecl *Std = S.getStdNamespace()) { + LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), + Loc, Sema::LookupOrdinaryName); + if (S.LookupQualifiedName(ResultSL, Std)) { + if (auto *SLDecl = ResultSL.getAsSingle()) { + LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), + Loc, Sema::LookupOrdinaryName); + if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && + S.LookupQualifiedName(ResultImpl, SLDecl)) { + ImplDecl = ResultImpl.getAsSingle(); + } + } + } + } + + if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { + S.Diag(Loc, diag::err_std_source_location_impl_not_found); + return nullptr; + } + + // Verify that __impl is a trivial struct type, with no base classes, and with + // only the four expected fields. + if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || + ImplDecl->getNumBases() != 0) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } + + unsigned Count = 0; + for (FieldDecl *F : ImplDecl->fields()) { + StringRef Name = F->getName(); + + if (Name == "_M_file_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) + break; + Count++; + } else if (Name == "_M_function_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) + break; + Count++; + } else if (Name == "_M_line") { + if (!F->getType()->isIntegerType()) + break; + Count++; + } else if (Name == "_M_column") { + if (!F->getType()->isIntegerType()) + break; + Count++; + } else { + Count = 100; // invalid + break; + } + } + if (Count != 4) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } + + return ImplDecl; +} + +ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + QualType ResultTy; + switch (Kind) { + case SourceLocIdentKind::File: + case SourceLocIdentKind::FileName: + case SourceLocIdentKind::Function: + case SourceLocIdentKind::FuncSig: { + QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); + ResultTy = + Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); + break; + } + case SourceLocIdentKind::Line: + case SourceLocIdentKind::Column: + ResultTy = Context.UnsignedIntTy; + break; + case SourceLocIdentKind::SourceLocStruct: + if (!StdSourceLocationImplDecl) { + StdSourceLocationImplDecl = + LookupStdSourceLocationImpl(*this, BuiltinLoc); + if (!StdSourceLocationImplDecl) + return ExprError(); + } + ResultTy = Context.getPointerType( + Context.getRecordType(StdSourceLocationImplDecl).withConst()); + break; + } + + return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return new (Context) + SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); +} + +ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc, + StringLiteral *BinaryData) { + EmbedDataStorage *Data = new (Context) EmbedDataStorage; + Data->BinaryData = BinaryData; + return new (Context) + EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0, + Data->getDataElementCount()); +} + +// Emits the +// `diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable` diagnostic. +// +// If the `__single` expression is a `DeclRefExpr` then a pointer to the +// corresponding Decl is returned, otherwise a nullptr is returned. +static ValueDecl * +DiagnoseBoundsSafetyImplicitConversionFromSingleToExplicitIndexable( + QualType DstType, QualType SrcType, Expr *SrcExpr, + Sema::AssignmentAction ActionForDiag, QualType FirstType, + QualType SecondType, PartialDiagnostic &FDiag) { + // We emit that a type is `__indexable` or + // `__bidi_indexable` only when necesary. + // 0 - emit `__indexable` + // 1 - emit `__bidi_indexable` + // 2 - Don't emit (DiagDstTypeMaybeEmpty only) + int DiagDstType = 0; + int DiagDstTypeMaybeEmpty = 0; + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstTypePtr = DstType->getUnqualifiedDesugaredType(); + const auto *DstPointerType = dyn_cast(DstTypePtr); + if (!DstPointerType) + llvm_unreachable("DstTypePtr should be a PointerType"); + + if (DstPointerType->isIndexable()) + DiagDstType = 0; + else if (DstPointerType->isBidiIndexable()) + DiagDstType = 1; + else + llvm_unreachable("DstPointerType has unexpected type"); + + auto IsNestedPtrType = [](const Type *Ty) -> bool { + if (const auto PtrType = + dyn_cast(Ty->getUnqualifiedDesugaredType())) { + if (isa( + PtrType->getPointeeType()->getUnqualifiedDesugaredType())) + return true; + } + return false; + }; + + if (IsNestedPtrType(DstPointerType)) { + // Nested Pointer. In this case explicitly state that it's + // an indexable type in the diagnostic to make this more obvious + // to the developer. + DiagDstTypeMaybeEmpty = DiagDstType; + } else { + // Don't emphasise the pointer type in the diagnostic to make + // it shorter. + DiagDstTypeMaybeEmpty = 2; + } + + // 0 - emit `__single` + // 1 - Don't emit + // Decide whether or not to emphaise that the pointer type is `__single`. + int DiagSrcTypeMaybeEmpty = IsNestedPtrType(SrcType.getTypePtr()) ? 0 : 1; + + FDiag << FirstType << SecondType << ActionForDiag << DiagDstTypeMaybeEmpty + << DiagSrcTypeMaybeEmpty << DiagDstType << SrcExpr->getSourceRange(); + + // Look at the __single pointer and see if it's a DeclRefExpr. In that case + // we can suggest to the developer to use `__counted_by`. + // TODO(dliew): We should also look for function calls that return `__single`. + // In that case we could suggest adding `__counted_by` to return type + // (rdar://91928583). + ValueDecl *SrcDecl = nullptr; + std::tie(SrcDecl, std::ignore) = shouldSuggestBoundsSafetyCountedBy(SrcExpr); + + // Decide whether or not to suggest using __counted_by. + if (SrcDecl) { + FDiag << 1 << SrcDecl->getQualifiedNameAsString(); + } else { + FDiag << 0; + } + return SrcDecl; +} + +static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, + const Expr *SrcExpr) { + if (!DstType->isFunctionPointerType() || + !SrcExpr->getType()->isFunctionType()) + return false; + + auto *DRE = dyn_cast(SrcExpr->IgnoreParenImpCasts()); + if (!DRE) + return false; + + auto *FD = dyn_cast(DRE->getDecl()); + if (!FD) + return false; + + return !S.checkAddressOfFunctionIsAvailable(FD, + /*Complain=*/true, + SrcExpr->getBeginLoc()); +} + +/* TO_UPSTREAM(BoundsSafety) ON*/ +static void TryFixAssigningSingleOpaquePtrToImplicitIndexablePtr( + const ValueDecl *Assignee, QualType DstType, QualType SrcType, Sema &S, + llvm::SmallVectorImpl> + &FixIts) { + // If a local bidi_indexable or global __bidi_indexable/__indexable pointer is + // being initialized from an opaque pointer suggest making it __single. This + // is the only sensible attribute because opaque types cannot be indexed. + + // Is the pointer attribute on the destination implicit? + bool IsAutoBoundPtr = DstType->hasAttr(attr::PtrAutoAttr); + + // TODO: rdar://114478465 + if (!IsAutoBoundPtr) + return; + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + assert(DstPointerType->isIndexable() || DstPointerType->isBidiIndexable()); + assert(SrcType->isSinglePointerType()); + + // Check if source is a pointer to an opaque type. + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *SrcPointerType = SrcType->getAs(); + if (!SrcPointerType->getPointeeType()->isIncompleteType()) + return; + + if (auto *AssignedVar = dyn_cast(Assignee)) { + // There may be multiple variable decls in the case of globals so we try + // to annotate all of them. + BoundsSafetyFixItUtils::CreateAnnotateAllPointerDeclsFixIts( + AssignedVar, "__single", S, FixIts); + } else if (auto *AssignedFieldDecl = dyn_cast(Assignee)) { + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + AssignedFieldDecl, "__single", S); + if (!FixIt.isNull()) + FixIts.emplace_back(std::make_tuple(FixIt, AssignedFieldDecl)); + } else { + llvm_unreachable("Unexpected ValueDecl sub type"); + } +} + +void Sema::TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr( + const ValueDecl *Assignee, Expr *SrcExpr, QualType DstType, AssignmentAction Action) { + // If a bidi_indexable pointer is being assigned from a null + // terminated pointer suggest making the local __bidi_indexable + // __null_terminated. + + // Is the pointer attribute on the destination implicit? + bool IsAutoBoundPtr = DstType->hasAttr(attr::PtrAutoAttr); + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + + if (!IsAutoBoundPtr || !DstPointerType->isBidiIndexable()) + return; + + const auto *SrcPointerType = + SrcExpr->IgnoreParenImpCasts()->getType()->getAs(); + if (!SrcPointerType->isValueTerminatedType()) + return; + if (!SrcPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + + // If the function return type is an implicit __bidi_indexable and the + // returned pointer is __null_terminated, suggest adding the attribute + // __null_terminated to the function return type. + if (Action == AA_Returning) { + auto *Caller = getCurFunctionDecl(); + if (!Caller) + return; + auto FnLoc = Caller->getReturnTypeSourceRange(); + if (FnLoc.isInvalid()) + return; + for (auto *I : Caller->redecls()) { + if (!I->getReturnType()->hasAttr(attr::PtrAutoAttr)) + return; + } + + auto RetLoc = FnLoc.getEnd().getLocWithOffset(1); + auto FixitDiag = Diag(RetLoc, diag::note_bounds_safety_consider_adding_to_return); + // FIXME: rdar://125936876 + for (auto *I : Caller->redecls()) { + auto IFnLoc = I->getNameInfo().getSourceRange(); + auto IRetLoc = IFnLoc.getBegin(); + auto FixIt = FixItHint::CreateInsertion(IRetLoc, "__null_terminated "); + FixitDiag << I->getDeclaredReturnType(); + FixitDiag << "__null_terminated"; + FixitDiag << I->getName().str(); + FixitDiag << FixIt; + } + return; + } + + + auto *AssignedVar = dyn_cast_or_null(Assignee); + if (!AssignedVar) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + AssignedVar, "__null_terminated", *this); + if (FixIt.isNull()) + return; + + // Emit a note about where the variable that a fix is suggested for is + // declared. + auto FixitDiag = Diag(AssignedVar->getInnerLocStart(), + diag::note_bounds_safety_consider_adding_to); + FixitDiag << AssignedVar->getName(); + FixitDiag << "__null_terminated"; + FixitDiag << FixIt; + + // FIXME: rdar://122434039 +} + +void Sema::TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr( + Expr *SrcExpr, QualType DstType) { + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + + auto IsLocalBidi = [](VarDecl *VD) { + if (!VD || !VD->isLocalVarDecl()) + return false; + auto SrcVarType = VD->getType(); + auto SrcPtr = SrcVarType->getAs(); + // We only support implicit __bidi_indexable to: + // 1. Make writing the fixit easier (it's purely additive). + // If there ways already an attribute in source code we'd need to + // remove it. + // 2. If the __bidi_indexable attribute is explicit someone probably + // added deliberately and that's a hint that we should respect their choice. + if (!SrcPtr || !SrcVarType->hasAttr(attr::PtrAutoAttr)) + return false; + if(!SrcPtr->isBidiIndexable()) + return false; + return true; + }; + + auto EmitFixitDiag = [&](FixItHint FixIt, DeclaratorDecl *SrcVar) { + if (FixIt.isNull()) + return; + auto FixitDiag = Diag(SrcVar->getInnerLocStart(), + diag::note_bounds_safety_consider_adding_to); + FixitDiag << SrcVar->getName(); + FixitDiag << "__null_terminated"; + FixitDiag << FixIt; + }; + + // If a local bidi_indexable pointer is being assigned to a null + // terminated pointer suggest changing the local __bidi_indexable + // to be __null_terminated. + if (auto *SrcDRE = dyn_cast(SrcExpr->IgnoreParens())) { + auto *SrcVD = SrcDRE->getDecl(); + auto *SrcVar = dyn_cast(SrcVD); + if (!IsLocalBidi(SrcVar)) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + SrcVar, "__null_terminated", *this); + EmitFixitDiag(FixIt, SrcVar); + return; + } + + // If a bidi_indexable const array in struct is being assigned to a null + // terminated pointer suggest changing the __bidi_indexable to be + // __null_terminated. + if (auto *SrcME = dyn_cast(SrcExpr->IgnoreParens())) { + auto *SrcVar = dyn_cast(SrcME->getMemberDecl()); + if (!SrcVar || !SrcVar->getType()->isConstantArrayType()) + return; + auto SrcArr = SrcVar->getTypeSourceInfo()->getTypeLoc() + .getAs(); + if (!SrcArr) + return; + auto FixIt = FixItHint::CreateInsertion(SrcArr.getLBracketLoc() + .getLocWithOffset(1), "__null_terminated "); + EmitFixitDiag(FixIt, SrcVar); + return; + } + + if (auto *SrcCastExpr = dyn_cast(SrcExpr->IgnoreParens())) { + if (SrcCastExpr->getCastKind() != clang::CK_BitCast + && SrcCastExpr->getCastKind() != clang::CK_NoOp) + return; + auto *SrcSE = SrcCastExpr->getSubExpr(); + if (!SrcSE) + return; + auto *SrcD = dyn_cast(SrcSE->IgnoreCasts()); + if (!SrcD) + return; + auto *SrcVar = dyn_cast(SrcD->getDecl()); + if (!IsLocalBidi(SrcVar)) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + SrcVar, "__null_terminated", *this); + EmitFixitDiag(FixIt, SrcVar); + } + + // FIXME: rdar://122434039 +} + +void Sema::TryFixAssigningBidiIndexableExprToNullTerminated(Expr *SrcExpr, + QualType DstType) { + // If a source expression with __bidi_indexable pointer type + // is being assigned to something with a __null_terminated pointer + // type suggest the __unsafe_null_terminated_from_indexable + // builtin to allow the conversion. + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + + QualType SrcType = SrcExpr->IgnoreParenImpCasts()->getType(); + if (!SrcType->isPointerTypeWithBounds() && !SrcType->isConstantArrayType()) + return; + + auto FixIt = FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), "__unsafe_null_terminated_from_indexable("); + if (FixIt.isNull()) + return; + + unsigned TkLen = Lexer::MeasureTokenLength(SrcExpr->getEndLoc(), + getSourceManager(), LangOpts); + { + auto FixitDiag = + Diag(SrcExpr->getBeginLoc(), + diag::note_fixit_unsafe_null_terminated_from_indexable); + FixitDiag << FixIt; + + FixitDiag << FixItHint::CreateInsertion(SrcExpr->getEndLoc() + .getLocWithOffset(TkLen), ")"); + } + + { + auto FixitDiag = + Diag(SrcExpr->getBeginLoc(), + diag::note_fixit_unsafe_null_terminated_from_indexable_ptr); + FixitDiag << FixIt; + FixitDiag << FixItHint::CreateInsertion(SrcExpr->getEndLoc().getLocWithOffset(TkLen), + ", <# pointer to null terminator #>)"); + } + + // FIXME: rdar://122434039 +} + +void Sema::TryFixAssigningNullTerminatedToBidiIndexableExpr(Expr *SrcExpr, + QualType DstType) { + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + if(!DstPointerType->isPointerTypeWithBounds()) + return; + + const auto *SrcPointerType = + SrcExpr->IgnoreParenImpCasts()->getType()->getAs(); + if (!SrcPointerType || !SrcPointerType->isValueTerminatedType()) + return; + + if (!SrcPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + auto EmitFixitDiag = [&](StringRef Code, unsigned DiagID, + int SelectFixitNote) { + assert(Code.ends_with("(")); + auto FixitDiag = Diag(SrcExpr->getBeginLoc(), DiagID) << SelectFixitNote; + auto FixIt = FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), Code); + if (FixIt.isNull()) + return; + FixitDiag << FixIt; + unsigned TkLen = Lexer::MeasureTokenLength(SrcExpr->getEndLoc(), + getSourceManager(), LangOpts); + FixitDiag << FixItHint::CreateInsertion( + SrcExpr->getEndLoc().getLocWithOffset(TkLen), ")"); + }; + EmitFixitDiag("__null_terminated_to_indexable(", + diag::note_fixit_null_terminated_to_indexable, 0); + EmitFixitDiag("__unsafe_null_terminated_to_indexable(", + diag::note_fixit_null_terminated_to_indexable, 1); + + return; + // FIXME: rdar://122434039 +} + +static std::tuple +TryFixNestedBoundsSafetyPointerAttributeMismatch(Expr *SrcExpr, + QualType DstType, + ASTContext &Context, Sema &S) { + //  If taking the address of a local variable, suggest adding pointer + // attributes to match the destination. Only suggest if the source's + // pointer attributes were automatically set and the destination has + // internal bounds. + auto LocalPointerThatHasAddressTaken = [](Expr *E) -> VarDecl * { + auto *AddrOf = dyn_cast(E->IgnoreParenImpCasts()); + if (!AddrOf || AddrOf->getOpcode() != UO_AddrOf) + return nullptr; + + auto *AddrOfOperand = + dyn_cast(AddrOf->getSubExpr()->IgnoreParenImpCasts()); + if (!AddrOfOperand) + return nullptr; + + auto *VD = dyn_cast(AddrOfOperand->getDecl()); + if (!VD || !VD->hasLocalStorage()) + return nullptr; + + if (!VD->getType()->hasAttr(attr::PtrAutoAttr)) + return nullptr; + + return VD; + }; + + auto NestedPointer = [](QualType QT) { + auto PPTy = QT->getAs(); + if (!PPTy) + return QualType(); + + auto PQTy = PPTy->getPointeeType(); + if (PQTy->getAs()) + return QualType(); + + if (!PQTy->getAs()) + return QualType(); + + return PQTy; + }; + + auto *VD = LocalPointerThatHasAddressTaken(SrcExpr); + + if (!VD) + return std::make_tuple(FixItHint(), nullptr); + auto InnerTy = NestedPointer(DstType); + if (InnerTy.isNull()) + return std::make_tuple(FixItHint(), nullptr); + + auto InnerAttrs = InnerTy->getAs()->getPointerAttributes(); + auto Corrected = Context.getBoundsSafetyPointerType(VD->getType(), InnerAttrs); + auto PtrToCorrected = Context.getPointerType( + Corrected, DstType->getAs()->getPointerAttributes()); + auto AssignType = + S.CheckAssignmentConstraints(SourceLocation(), DstType, PtrToCorrected); + + if (AssignType != Sema::Compatible) + return std::make_tuple(FixItHint(), nullptr); + + bool NeedsSpaceAfterKeyword; + SourceLocation FixItLoc; + std::tie(FixItLoc, NeedsSpaceAfterKeyword) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint( + VD->getTypeSourceInfo()->getTypeLoc(), S); + + if (FixItLoc.isInvalid()) + return std::make_tuple(FixItHint(), nullptr); + + StringRef Keyword; + if (InnerAttrs.isSingle()) + Keyword = "__single "; + else if (InnerAttrs.isIndexable()) + Keyword = "__indexable "; + else if (InnerAttrs.isBidiIndexable()) + Keyword = "__bidi_indexable "; + else if (InnerAttrs.isUnsafeIndexable()) + Keyword = "__unsafe_indexable "; + + if (Keyword.empty()) + return std::make_tuple(FixItHint(), nullptr); + + StringRef NoSpace = Keyword.drop_back(); + if (!NeedsSpaceAfterKeyword) + Keyword = NoSpace; + if (!S.PP.isMacroDefined(NoSpace)) + return std::make_tuple(FixItHint(), nullptr); + + return std::make_tuple(FixItHint::CreateInsertion(FixItLoc, Keyword), VD); +} + +static const ValueDecl *ReferencedValueDecl(const Expr *E) { + const DeclRefExpr *DRef = nullptr; + + if (auto *DRE = dyn_cast(E->IgnoreParenImpCasts())) { + DRef = DRE; + } else if (const auto *ME = dyn_cast(E)) { + return dyn_cast(ME->getMemberDecl()); + } else if (const auto *AE = + dyn_cast(E->IgnoreParenImpCasts())) { + const auto *BaseExpr = AE->getBase()->IgnoreParenImpCasts(); + if (const auto *ME = dyn_cast(BaseExpr)) + return dyn_cast(ME->getMemberDecl()); + DRef = dyn_cast(BaseExpr); + } + + if (!DRef) + return nullptr; + + return dyn_cast(DRef->getDecl()); +} + +void Sema::TryFixAssigningConstArrayToNullTerminated(const Expr *SrcExpr, + QualType SrcType, + QualType DstType) { + const auto *DstPointerType = DstType->getAs(); + + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + + // Check if this is a constant sized array. + if (!SrcType->isConstantArrayType()) + return; + + const auto *Decl = ReferencedValueDecl(SrcExpr); + + if (!Decl) + return; + + const VarDecl *VD = dyn_cast(Decl); + + if (!VD) + return; + + bool InitializerTerminatedWithNull = false; + + if (VD->hasInit()) { + const Expr *e = VD->getInit()->IgnoreParenImpCasts(); + if (const auto InitExpr = dyn_cast(e)) { + // Check if the intializer end with a null pointer constant. If yes, + // suggest adding appropriate attribute to the decl. + bool isInitExprEmpty = InitExpr->inits().empty(); + if (!isInitExprEmpty && + !InitExpr->inits().back()->isNullPointerConstant( + getASTContext(), Expr::NPC_ValueDependentIsNull)) + return; + InitializerTerminatedWithNull = true; + } else if (const auto StrLiteral = dyn_cast(e)) { + const auto *SrcArrayType = Context.getAsConstantArrayType(SrcType); + // The initializations via StringLiterals are always terminated by if the + // intializer length is smaller than the size of the array being + // initialized + if (SrcArrayType->getSize().ule(StrLiteral->getLength())) + return; + InitializerTerminatedWithNull = true; + } else + return; + } + + SourceLocation FixItLoc = VD->getLocation(); + FixItHint NonTerminatedByToTerminatedByFixIt; + + // Suggest adding const qualifier to local arrays that are initialized with + // a null terminator. Adding const to such arrays allows them to be + // implicitly converted to a __null_terminated pointer. In all other cases + // implicit conversion is forbidden. This suggestion is only made for local + // arrays because suggesting it other locations (e.g. function parameters + // and global variables) has a higher chance of breaking compilation, + // especially for non bounds-safety code where adding const changes semantics + // but __null_terminated doesn't change semantics of non fbounds-safety code. + if (InitializerTerminatedWithNull && VD->hasLocalStorage() && + DstType->getPointeeType().isConstQualified()) { + FixItLoc = VD->getBeginLoc(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "const "); + } else { + std::string Token = "__null_terminated"; + TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc(); + if (ArrayTypeLoc ATL = TL.getAs()) { + FixItLoc = ATL.getLBracketLoc().getLocWithOffset(1); + // Add space if there is a size expression + if (ATL.getSizeExpr()) + Token += " "; + } else if(SrcType->isTypedefNameType()) { + Token += " "; + } + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, Token); + } + + if (NonTerminatedByToTerminatedByFixIt.isNull()) + return; + + auto FixitDiag = + Diag(VD->getBeginLoc(), diag::note_bounds_safety_consider_adding_to); + auto AddedAttr = + llvm::StringRef(NonTerminatedByToTerminatedByFixIt.CodeToInsert).trim(); + FixitDiag << VD->getName() << AddedAttr; + FixitDiag << NonTerminatedByToTerminatedByFixIt; +} + +void Sema::TryFixAssigningSinglePtrToNullTerminated(Expr *SrcExpr, + QualType SrcType, + QualType DstType) { + auto *VD = ReferencedValueDecl(SrcExpr); + + if (!VD) + return; + + auto IsReallySinglePointer = [](const QualType Ty) -> bool { + if (!Ty->isSinglePointerType()) + return false; + + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; + + return true; + }; + + if (!IsReallySinglePointer(SrcType)) + return; + + // If the `__single` is explicit don't suggest a fix-it because + // the user manually added this attribute which is a hint that + // they know what they are doing. + if (!SrcType->hasAttr(attr::PtrAutoAttr)) + return; + + // TODO: This should be handled by + // BoundsSafetyFixItUtils::FindPointerAttrInsertPoint rdar://123659361 + SourceLocation FixItLoc; + + FixItHint NonTerminatedByToTerminatedByFixIt; + + if (auto *VTT = dyn_cast(DstType)) + if (!VTT->getPointeeType()->isAnyCharacterType() || + !VTT->getTerminatorValue(Context).isZero()) + return; + + // If the destination type is a pointer to a const qualified type, then + // suggest making the type pointed by SrcType const qualified. Otherwise, + // suggest qualifying SrcType with __null_terminated. + if (DstType->getPointeeType().isConstQualified()) { + FixItLoc = VD->getBeginLoc(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "const "); + } else { + FixItLoc = VD->getLocation(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "__null_terminated "); + } + + if (VD && !NonTerminatedByToTerminatedByFixIt.isNull()) { + auto FixitDiag = + Diag(VD->getBeginLoc(), diag::note_bounds_safety_consider_adding_to); + auto AddedAttr = + llvm::StringRef(NonTerminatedByToTerminatedByFixIt.CodeToInsert).trim(); + FixitDiag << VD->getName() << AddedAttr; + FixitDiag << NonTerminatedByToTerminatedByFixIt; + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, + SourceLocation Loc, + QualType DstType, QualType SrcType, + Expr *SrcExpr, AssignmentAction Action, + bool *Complained, + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *Assignee) { + if (Complained) + *Complained = false; + + // Decode the result (notice that AST's are still created for extensions). + bool CheckInferredResultType = false; + bool isInvalid = false; + unsigned DiagKind = 0; + ConversionFixItGenerator ConvHints; + bool MayHaveConvFixit = false; + bool MayHaveFunctionDiff = false; + bool IsSrcNullTerm = false; + bool IsDstNullTerm = false; + const ObjCInterfaceDecl *IFace = nullptr; + const ObjCProtocolDecl *PDecl = nullptr; + /*TO_UPSTREAM(BoundsSafety) ON*/ + llvm::SmallVector, 1> + AssignedVarFixIts; + VarDecl *BoundsSafetyIncompatNestedPtrLocalVarToFix = nullptr; + FixItHint BoundsSafetyIncompatNestedPtrFixIt; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + + + switch (ConvTy) { + case Compatible: + DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr); + return false; + + case PointerToInt: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_pointer_int; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_pointer_int; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case IncompatibleIntToSafePointer: + DiagKind = diag::err_bounds_safety_non_to_pointer; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleUnsafeToIndexablePointer: { + DiagKind = diag::err_bounds_safety_unsafe_to_safe; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; + // Fix up SrcType to show __unsafe_indexable + auto SrcPTy = SrcType->getAs(); + if (SrcPTy && !SrcPTy->isUnsafeIndexable()) { + SrcType = Context.getPointerType( + SrcPTy->getPointeeType(), + BoundsSafetyPointerAttributes::unsafeIndexable()); + } + break; + } + case IncompatibleUnsafeToSafePointer: { + DiagKind = diag::err_bounds_safety_unsafe_to_safe; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; + break; + } + case IncompleteSingleToIndexablePointer: { + DiagKind = diag::err_bounds_safety_incomplete_single_to_indexable; + isInvalid = true; + // Can these FixIts ever fire? + bool ConvFixitEmitted = + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; + + // Guard with `!ConvFixitEmitted` because we probably don't want these + // FixIts interacting. + if (!ConvFixitEmitted && Assignee) { + TryFixAssigningSingleOpaquePtrToImplicitIndexablePtr( + Assignee, DstType, SrcType, *this, AssignedVarFixIts); + } + break; + } + case CompatibleSingleToExplicitIndexablePointer: { + DiagKind = diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable; + isInvalid = false; + MayHaveConvFixit = false; + break; + } + case IncompatibleStringLiteralToValueTerminatedPointer: + DiagKind = diag::err_bounds_safety_incompatible_string_literal_to_terminated_by; + isInvalid = true; + break; + case IncompatibleValueTerminatedTerminators: + DiagKind = diag::err_bounds_safety_incompatible_terminated_by_terminators; + isInvalid = true; + break; + case IncompatibleValueTerminatedToNonValueTerminatedPointer: { + const auto *SrcPointerType = SrcType->getAs(); + IsSrcNullTerm = + SrcPointerType->getTerminatorValue(getASTContext()).isZero(); + DiagKind = + diag::err_bounds_safety_incompatible_terminated_by_to_non_terminated_by; + isInvalid = true; + break; + } + case IncompatibleNonValueTerminatedToValueTerminatedPointer: { + const auto *DstPointerType = DstType->getAs(); + IsDstNullTerm = + DstPointerType->getTerminatorValue(getASTContext()).isZero(); + if (getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = + diag::warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = false; + } else { + DiagKind = + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = true; + } + break; + } + case IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch; + isInvalid = true; + break; + case IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + if (getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = true; + } + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + case IntToPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_int_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_int_pointer; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleFunctionPointerStrict: + DiagKind = + diag::warn_typecheck_convert_incompatible_function_pointer_strict; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleFunctionPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_function_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_function_pointer; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatiblePointer: + if (Action == AA_Passing_CFAudited) { + DiagKind = diag::err_arc_typecheck_convert_incompatible_pointer; + } else if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_pointer; + } + CheckInferredResultType = DstType->isObjCObjectPointerType() && + SrcType->isObjCObjectPointerType(); + if (CheckInferredResultType) { + SrcType = SrcType.getUnqualifiedType(); + DstType = DstType.getUnqualifiedType(); + } else { + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + } + MayHaveConvFixit = true; + break; + case IncompatiblePointerSign: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_pointer_sign; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_pointer_sign; + } + break; + case FunctionVoidPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_pointer_void_func; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_pointer_void_func; + } + break; + case IncompatiblePointerDiscardsQualifiers: { + // Perform array-to-pointer decay if necessary. + if (SrcType->isArrayType()) SrcType = Context.getArrayDecayedType(SrcType); + + isInvalid = true; + + Qualifiers lhq = SrcType->getPointeeType().getQualifiers(); + Qualifiers rhq = DstType->getPointeeType().getQualifiers(); + if (lhq.getAddressSpace() != rhq.getAddressSpace()) { + DiagKind = diag::err_typecheck_incompatible_address_space; + break; + } else if (lhq.getPointerAuth() != rhq.getPointerAuth()) { + DiagKind = diag::err_typecheck_incompatible_ptrauth; + break; + } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { + DiagKind = diag::err_typecheck_incompatible_ownership; + break; + } + + llvm_unreachable("unknown error case for discarding qualifiers!"); + // fallthrough + } + case CompatiblePointerDiscardsQualifiers: + // If the qualifiers lost were because we were applying the + // (deprecated) C++ conversion from a string literal to a char* + // (or wchar_t*), then there was no error (C++ 4.2p2). FIXME: + // Ideally, this check would be performed in + // checkPointerTypesForAssignment. However, that would require a + // bit of refactoring (so that the second argument is an + // expression, rather than a type), which should be done as part + // of a larger effort to fix checkPointerTypesForAssignment for + // C++ semantics. + if (getLangOpts().CPlusPlus && + IsStringLiteralToNonConstPointerConversion(SrcExpr, DstType)) + return false; + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_discards_qualifiers; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_discards_qualifiers; + } + + break; + case IncompatibleNestedPointerQualifiers: + if (getLangOpts().CPlusPlus) { + isInvalid = true; + DiagKind = diag::err_nested_pointer_qualifier_mismatch; + } else { + DiagKind = diag::ext_nested_pointer_qualifier_mismatch; + } + break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case IncompatibleNestedBoundsSafetyPointerAttributes: { + DiagKind = diag::err_nested_bounds_safety_pointer_attribute_mismatch; + isInvalid = true; + + std::tie(BoundsSafetyIncompatNestedPtrFixIt, + BoundsSafetyIncompatNestedPtrLocalVarToFix) = + TryFixNestedBoundsSafetyPointerAttributeMismatch(SrcExpr, DstType, + Context, *this); + break; + } + case IncompatibleBoundsSafetyFunctionPointer: + DiagKind = diag::err_incompatible_bounds_safety_function_pointer; + isInvalid = true; + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + case IncompatibleNestedPointerAddressSpaceMismatch: + DiagKind = diag::err_typecheck_incompatible_nested_address_space; + isInvalid = true; + break; + case IntToBlockPointer: + DiagKind = diag::err_int_to_block_pointer; + isInvalid = true; + break; + case IncompatibleBlockPointer: + DiagKind = diag::err_typecheck_convert_incompatible_block_pointer; + isInvalid = true; + break; + case IncompatibleObjCQualifiedId: { + if (SrcType->isObjCQualifiedIdType()) { + const ObjCObjectPointerType *srcOPT = + SrcType->castAs(); + for (auto *srcProto : srcOPT->quals()) { + PDecl = srcProto; + break; + } + if (const ObjCInterfaceType *IFaceT = + DstType->castAs()->getInterfaceType()) + IFace = IFaceT->getDecl(); + } + else if (DstType->isObjCQualifiedIdType()) { + const ObjCObjectPointerType *dstOPT = + DstType->castAs(); + for (auto *dstProto : dstOPT->quals()) { + PDecl = dstProto; + break; + } + if (const ObjCInterfaceType *IFaceT = + SrcType->castAs()->getInterfaceType()) + IFace = IFaceT->getDecl(); + } + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_incompatible_qualified_id; + isInvalid = true; + } else { + DiagKind = diag::warn_incompatible_qualified_id; + } + break; + } + case IncompatibleVectors: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_incompatible_vectors; + isInvalid = true; + } else { + DiagKind = diag::warn_incompatible_vectors; + } + break; + case IncompatibleObjCWeakRef: + DiagKind = diag::err_arc_weak_unavailable_assign; + isInvalid = true; + break; + case Incompatible: + if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { + if (Complained) + *Complained = true; + return true; + } + + DiagKind = diag::err_typecheck_convert_incompatible; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + isInvalid = true; + MayHaveFunctionDiff = true; + break; + } + + QualType FirstType, SecondType; + switch (Action) { + case AA_Assigning: + case AA_Initializing: + // The destination type comes first. + FirstType = DstType; + SecondType = SrcType; + break; + + case AA_Returning: + case AA_Passing: + case AA_Passing_CFAudited: + case AA_Converting: + case AA_Sending: + case AA_Casting: + // The source type comes first. + FirstType = SrcType; + SecondType = DstType; + break; + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + PartialDiagnostic FDiag = PDiag(DiagKind); + AssignmentAction ActionForDiag = Action; + if (Action == AA_Passing_CFAudited) + ActionForDiag = AA_Passing; + + ValueDecl *SrcDecl = nullptr; + if (DiagKind == diag::err_bounds_safety_incomplete_single_to_indexable) { + std::string AssigneeName(""); + if (Assignee) + AssigneeName = Assignee->getQualifiedNameAsString(); + FDiag << FirstType << SecondType << ActionForDiag << AssigneeName + << /* NonEmptyName */ (AssigneeName.size() > 0) + << SrcExpr->getSourceRange(); + } else if (DiagKind == + diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable) { + SrcDecl = DiagnoseBoundsSafetyImplicitConversionFromSingleToExplicitIndexable( + DstType, SrcType, SrcExpr, ActionForDiag, FirstType, SecondType, FDiag); + } else if ( + DiagKind == + diag::err_bounds_safety_incompatible_terminated_by_to_non_terminated_by) { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange() + << (IsSrcNullTerm ? /*null_terminated*/ 1 : /*terminated_by*/ 0); + } else if ( + DiagKind == + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by || + DiagKind == + diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by) { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange() << + (IsDstNullTerm ? /*null_terminated*/ 1 : /*terminated_by*/ 0); + } else { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + if (DiagKind == diag::ext_typecheck_convert_incompatible_pointer_sign || + DiagKind == diag::err_typecheck_convert_incompatible_pointer_sign) { + auto isPlainChar = [](const clang::Type *Type) { + return Type->isSpecificBuiltinType(BuiltinType::Char_S) || + Type->isSpecificBuiltinType(BuiltinType::Char_U); + }; + FDiag << (isPlainChar(FirstType->getPointeeOrArrayElementType()) || + isPlainChar(SecondType->getPointeeOrArrayElementType())); + } + + // If we can fix the conversion, suggest the FixIts. + if (!ConvHints.isNull()) { + for (FixItHint &H : ConvHints.Hints) + FDiag << H; + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Add FixIts to the error diagnostic if FixIts for this VarDecl haven't been + // emitted before. + bool AssignedVarFixItGoesOnError = false; + if (Assignee) { + AssignedVarFixItGoesOnError = + !AssignedVarFixIts.empty() && + !BoundsSafetyFixItWasEmittedFor(cast(Assignee)); + if (AssignedVarFixItGoesOnError) { + bool AssignedVarFixItsContainedAssignee = false; + for (auto &HintPair : AssignedVarFixIts) { + auto *VarToFix = std::get(HintPair); + // The FixIts we receive might actually fix multiple variables + // so we have to check for each one if a FixIt was already emitted + if (!BoundsSafetyFixItWasEmittedFor(VarToFix)) { + FDiag << std::get(HintPair); + // Record that a FixIt was emitted so that if another diagnostic is + // emitted we avoid trying to fix it again. Applying the same FixIt + // multiple times (i.e. with `-Xclang -fixit`) will result in invalid + // code. + BoundsSafetyFixItWasEmittedFor(VarToFix, + /*Set=*/true); + } + if (VarToFix == Assignee) + AssignedVarFixItsContainedAssignee = true; + } + assert(AssignedVarFixItsContainedAssignee); + (void)AssignedVarFixItsContainedAssignee; + } + } + + // Add Fixits to the error diagnostic for a local var that can be fixed. + bool BoundsSafetyIncompatNestedPtrFixItGoesOnError = false; + if (BoundsSafetyIncompatNestedPtrLocalVarToFix) { + BoundsSafetyIncompatNestedPtrFixItGoesOnError = + !BoundsSafetyIncompatNestedPtrFixIt.isNull() && + !BoundsSafetyFixItWasEmittedFor( + BoundsSafetyIncompatNestedPtrLocalVarToFix); + if (BoundsSafetyIncompatNestedPtrFixItGoesOnError) { + FDiag << BoundsSafetyIncompatNestedPtrFixIt; + // Record that a FixIt was emitted so that if another diagnostic is + // emitted we avoid trying to fix it again. Applying the same FixIt + // multiple times (i.e. with `-Xclang -fixit`) will result in invalid + // code. + BoundsSafetyFixItWasEmittedFor(BoundsSafetyIncompatNestedPtrLocalVarToFix, + /*Set=*/true); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + + if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); } + + if (MayHaveFunctionDiff) + HandleFunctionTypeMismatch(FDiag, SecondType, FirstType); + + Diag(Loc, FDiag); + if ((DiagKind == diag::warn_incompatible_qualified_id || + DiagKind == diag::err_incompatible_qualified_id) && + PDecl && IFace && !IFace->hasDefinition()) + Diag(IFace->getLocation(), diag::note_incomplete_class_and_qualified_id) + << IFace << PDecl; + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (DiagKind == + diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable && + SrcDecl) { + // Note where declaration of __single pointer is declared. + Diag(SrcDecl->getBeginLoc(), diag::note_pointer_declared_here_quoted) + << SrcDecl->getQualifiedNameAsString() << SrcDecl->getSourceRange(); + + // TODO(dliew): We should note when the __single pointer is implicitly + // __single (rdar://91928812). + } + + if (DiagKind == diag::err_bounds_safety_incomplete_single_to_indexable && + Assignee) { + // Emit a note telling the user where the assigned pointer is declared. + const NamedDecl *AssignedVar = cast(Assignee); + std::string AssigneeName = AssignedVar->getQualifiedNameAsString(); + // We try to omit the note when the VarDecl being assigned to is + // a function parameter because the `diag::note_parameter_named_here` + // is already emitted for it. An exception is made if there are FixIts + // available that need to go on a note. + if (Action != AA_Passing || + (Action == AA_Passing && AssignedVarFixIts.size() > 0 && + !AssignedVarFixItGoesOnError)) { + auto NoteDiag = Diag(AssignedVar->getBeginLoc(), + AssigneeName.size() > 0 + ? diag::note_pointer_declared_here_quoted + : diag::note_unnamed_pointer_declared_here); + if (AssigneeName.size() > 0) + NoteDiag << AssigneeName; + + NoteDiag << AssignedVar->getSourceRange(); + + if (!AssignedVarFixItGoesOnError) { + // Emit the FixIts on a note instead because a diagnostic with a FixIt + // for the this VarDecl was already emitted on an error diagnostic. + // + // The are several reasons for emitting the FixIts on a note: + // + // 1. Using `-Xclang -fixit` will only apply the fix to VarDecl once + // because only the first error diagnostic has the FixIt attached + // directly to it. Applying it multiple times would have resulted in + // broken code. + // 2. In interactive use cases (e.g. in an IDE) if the user is not + // looking at the first error diagnostic then they can still apply the + // FixIt by applying the FixIt attached to the note that is attached to + // the error diagnostic. + for (auto &HintPair : AssignedVarFixIts) + NoteDiag << std::get(HintPair); + } + } + } + + if (DiagKind == diag::err_nested_bounds_safety_pointer_attribute_mismatch) { + if (!BoundsSafetyIncompatNestedPtrFixIt.isNull()) { + // Emit a note about where the variable that a fix is suggested for is + // declared. + assert(BoundsSafetyIncompatNestedPtrLocalVarToFix); + auto FixitDiag = + Diag(BoundsSafetyIncompatNestedPtrLocalVarToFix->getInnerLocStart(), + diag::note_entity_declared_at); + FixitDiag << BoundsSafetyIncompatNestedPtrLocalVarToFix->getDeclName(); + + if (!BoundsSafetyIncompatNestedPtrFixItGoesOnError) { + // Emit the FixIts on a note instead because a diagnostic with a FixIt + // for the this VarDecl was already emitted on an error diagnostic. + FixitDiag << BoundsSafetyIncompatNestedPtrFixIt; + } + } + } + + if (IsSrcNullTerm) { + TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr(Assignee, SrcExpr, + DstType, Action); + + TryFixAssigningNullTerminatedToBidiIndexableExpr(SrcExpr, DstType); + } + + if (IsDstNullTerm) { + TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(SrcExpr, DstType); + + TryFixAssigningBidiIndexableExprToNullTerminated(SrcExpr, DstType); + + TryFixAssigningSinglePtrToNullTerminated(SrcExpr, SrcType, DstType); + + TryFixAssigningConstArrayToNullTerminated(SrcExpr, SrcType, DstType); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + if (SecondType == Context.OverloadTy) + NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression, + FirstType, /*TakingAddress=*/true); + + if (CheckInferredResultType) + ObjC().EmitRelatedResultTypeNote(SrcExpr); + + if (Action == AA_Returning && ConvTy == IncompatiblePointer) + ObjC().EmitRelatedResultTypeNoteForReturn(DstType); + + if (Complained) + *Complained = true; + return isInvalid; +} + +ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, + llvm::APSInt *Result, + AllowFoldKind CanFold) { + class SimpleICEDiagnoser : public VerifyICEDiagnoser { + public: + SemaDiagnosticBuilder diagnoseNotICEType(Sema &S, SourceLocation Loc, + QualType T) override { + return S.Diag(Loc, diag::err_ice_not_integral) + << T << S.LangOpts.CPlusPlus; + } + SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { + return S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus; + } + } Diagnoser; + + return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); +} + +ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, + llvm::APSInt *Result, + unsigned DiagID, + AllowFoldKind CanFold) { + class IDDiagnoser : public VerifyICEDiagnoser { + unsigned DiagID; + + public: + IDDiagnoser(unsigned DiagID) + : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { } + + SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { + return S.Diag(Loc, DiagID); + } + } Diagnoser(DiagID); + + return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); +} + +Sema::SemaDiagnosticBuilder +Sema::VerifyICEDiagnoser::diagnoseNotICEType(Sema &S, SourceLocation Loc, + QualType T) { + return diagnoseNotICE(S, Loc); +} + +Sema::SemaDiagnosticBuilder +Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc) { + return S.Diag(Loc, diag::ext_expr_not_ice) << S.LangOpts.CPlusPlus; +} + +ExprResult +Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, + VerifyICEDiagnoser &Diagnoser, + AllowFoldKind CanFold) { + SourceLocation DiagLoc = E->getBeginLoc(); + + if (getLangOpts().CPlusPlus11) { + // C++11 [expr.const]p5: + // If an expression of literal class type is used in a context where an + // integral constant expression is required, then that class type shall + // have a single non-explicit conversion function to an integral or + // unscoped enumeration type + ExprResult Converted; + class CXX11ConvertDiagnoser : public ICEConvertDiagnoser { + VerifyICEDiagnoser &BaseDiagnoser; + public: + CXX11ConvertDiagnoser(VerifyICEDiagnoser &BaseDiagnoser) + : ICEConvertDiagnoser(/*AllowScopedEnumerations*/ false, + BaseDiagnoser.Suppress, true), + BaseDiagnoser(BaseDiagnoser) {} + + SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) override { + return BaseDiagnoser.diagnoseNotICEType(S, Loc, T); + } + + SemaDiagnosticBuilder diagnoseIncomplete( + Sema &S, SourceLocation Loc, QualType T) override { + return S.Diag(Loc, diag::err_ice_incomplete_type) << T; + } + + SemaDiagnosticBuilder diagnoseExplicitConv( + Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { + return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy; + } + + SemaDiagnosticBuilder noteExplicitConv( + Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { + return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) + << ConvTy->isEnumeralType() << ConvTy; + } + + SemaDiagnosticBuilder diagnoseAmbiguous( + Sema &S, SourceLocation Loc, QualType T) override { + return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T; + } + + SemaDiagnosticBuilder noteAmbiguous( + Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) << ConvTy->isEnumeralType() << ConvTy; } - SemaDiagnosticBuilder diagnoseConversion( - Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { - llvm_unreachable("conversion functions are permitted"); - } - } ConvertDiagnoser(Diagnoser); + SemaDiagnosticBuilder diagnoseConversion( + Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { + llvm_unreachable("conversion functions are permitted"); + } + } ConvertDiagnoser(Diagnoser); + + Converted = PerformContextualImplicitConversion(DiagLoc, E, + ConvertDiagnoser); + if (Converted.isInvalid()) + return Converted; + E = Converted.get(); + // The 'explicit' case causes us to get a RecoveryExpr. Give up here so we + // don't try to evaluate it later. We also don't want to return the + // RecoveryExpr here, as it results in this call succeeding, thus callers of + // this function will attempt to use 'Value'. + if (isa(E)) + return ExprError(); + if (!E->getType()->isIntegralOrUnscopedEnumerationType()) + return ExprError(); + } else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) { + // An ICE must be of integral or unscoped enumeration type. + if (!Diagnoser.Suppress) + Diagnoser.diagnoseNotICEType(*this, DiagLoc, E->getType()) + << E->getSourceRange(); + return ExprError(); + } + + ExprResult RValueExpr = DefaultLvalueConversion(E); + if (RValueExpr.isInvalid()) + return ExprError(); + + E = RValueExpr.get(); + + // Circumvent ICE checking in C++11 to avoid evaluating the expression twice + // in the non-ICE case. + if (!getLangOpts().CPlusPlus11 && E->isIntegerConstantExpr(Context)) { + SmallVector Notes; + if (Result) + *Result = E->EvaluateKnownConstIntCheckOverflow(Context, &Notes); + if (!isa(E)) + E = Result ? ConstantExpr::Create(Context, E, APValue(*Result)) + : ConstantExpr::Create(Context, E); + + if (Notes.empty()) + return E; + + // If our only note is the usual "invalid subexpression" note, just point + // the caret at its location rather than producing an essentially + // redundant note. + if (Notes.size() == 1 && Notes[0].second.getDiagID() == + diag::note_invalid_subexpr_in_const_expr) { + DiagLoc = Notes[0].first; + Notes.clear(); + } + + if (getLangOpts().CPlusPlus) { + if (!Diagnoser.Suppress) { + Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); + } + return ExprError(); + } + + Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); + + return E; + } + + Expr::EvalResult EvalResult; + SmallVector Notes; + EvalResult.Diag = &Notes; + + // Try to evaluate the expression, and produce diagnostics explaining why it's + // not a constant expression as a side-effect. + bool Folded = + E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) && + EvalResult.Val.isInt() && !EvalResult.HasSideEffects && + (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); + + if (!isa(E)) + E = ConstantExpr::Create(Context, E, EvalResult.Val); + + // In C++11, we can rely on diagnostics being produced for any expression + // which is not a constant expression. If no diagnostics were produced, then + // this is a constant expression. + if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) { + if (Result) + *Result = EvalResult.Val.getInt(); + return E; + } + + // If our only note is the usual "invalid subexpression" note, just point + // the caret at its location rather than producing an essentially + // redundant note. + if (Notes.size() == 1 && Notes[0].second.getDiagID() == + diag::note_invalid_subexpr_in_const_expr) { + DiagLoc = Notes[0].first; + Notes.clear(); + } + + if (!Folded || !CanFold) { + if (!Diagnoser.Suppress) { + Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); + } + + return ExprError(); + } + + Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); + + if (Result) + *Result = EvalResult.Val.getInt(); + return E; +} + +namespace { + // Handle the case where we conclude a expression which we speculatively + // considered to be unevaluated is actually evaluated. + class TransformToPE : public TreeTransform { + typedef TreeTransform BaseTransform; + + public: + TransformToPE(Sema &SemaRef) : BaseTransform(SemaRef) { } + + // Make sure we redo semantic analysis + bool AlwaysRebuild() { return true; } + bool ReplacingOriginal() { return true; } + + // We need to special-case DeclRefExprs referring to FieldDecls which + // are not part of a member pointer formation; normal TreeTransforming + // doesn't catch this case because of the way we represent them in the AST. + // FIXME: This is a bit ugly; is it really the best way to handle this + // case? + // + // Error on DeclRefExprs referring to FieldDecls. + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + if (isa(E->getDecl()) && + !SemaRef.isUnevaluatedContext()) + return SemaRef.Diag(E->getLocation(), + diag::err_invalid_non_static_member_use) + << E->getDecl() << E->getSourceRange(); + + return BaseTransform::TransformDeclRefExpr(E); + } + + // Exception: filter out member pointer formation + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() == UO_AddrOf && E->getType()->isMemberPointerType()) + return E; + + return BaseTransform::TransformUnaryOperator(E); + } + + // The body of a lambda-expression is in a separate expression evaluation + // context so never needs to be transformed. + // FIXME: Ideally we wouldn't transform the closure type either, and would + // just recreate the capture expressions and lambda expression. + StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) { + return SkipLambdaBody(E, Body); + } + }; +} + +ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) { + assert(isUnevaluatedContext() && + "Should only transform unevaluated expressions"); + ExprEvalContexts.back().Context = + ExprEvalContexts[ExprEvalContexts.size()-2].Context; + if (isUnevaluatedContext()) + return E; + return TransformToPE(*this).TransformExpr(E); +} + +TypeSourceInfo *Sema::TransformToPotentiallyEvaluated(TypeSourceInfo *TInfo) { + assert(isUnevaluatedContext() && + "Should only transform unevaluated expressions"); + ExprEvalContexts.back().Context = parentEvaluationContext().Context; + if (isUnevaluatedContext()) + return TInfo; + return TransformToPE(*this).TransformType(TInfo); +} + +void +Sema::PushExpressionEvaluationContext( + ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, + ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { + ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, + LambdaContextDecl, ExprContext); + + // Discarded statements and immediate contexts nested in other + // discarded statements or immediate context are themselves + // a discarded statement or an immediate context, respectively. + ExprEvalContexts.back().InDiscardedStatement = + parentEvaluationContext().isDiscardedStatementContext(); + + // C++23 [expr.const]/p15 + // An expression or conversion is in an immediate function context if [...] + // it is a subexpression of a manifestly constant-evaluated expression or + // conversion. + const auto &Prev = parentEvaluationContext(); + ExprEvalContexts.back().InImmediateFunctionContext = + Prev.isImmediateFunctionContext() || Prev.isConstantEvaluated(); + + ExprEvalContexts.back().InImmediateEscalatingFunctionContext = + Prev.InImmediateEscalatingFunctionContext; + + Cleanup.reset(); + if (!MaybeODRUseExprs.empty()) + std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); +} + +void +Sema::PushExpressionEvaluationContext( + ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t, + ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { + Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl; + PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); +} + +namespace { + +const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { + PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); + if (const auto *E = dyn_cast(PossibleDeref)) { + if (E->getOpcode() == UO_Deref) + return CheckPossibleDeref(S, E->getSubExpr()); + } else if (const auto *E = dyn_cast(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto *E = dyn_cast(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto E = dyn_cast(PossibleDeref)) { + QualType Inner; + QualType Ty = E->getType(); + if (const auto *Ptr = Ty->getAs()) + Inner = Ptr->getPointeeType(); + else if (const auto *Arr = S.Context.getAsArrayType(Ty)) + Inner = Arr->getElementType(); + else + return nullptr; + + if (Inner->hasAttr(attr::NoDeref)) + return E; + } + return nullptr; +} + +} // namespace + +void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { + for (const Expr *E : Rec.PossibleDerefs) { + const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); + if (DeclRef) { + const ValueDecl *Decl = DeclRef->getDecl(); + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << Decl->getName() << E->getSourceRange(); + Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); + } else { + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) + << E->getSourceRange(); + } + } + Rec.PossibleDerefs.clear(); +} + +void Sema::CheckUnusedVolatileAssignment(Expr *E) { + if (!E->getType().isVolatileQualified() || !getLangOpts().CPlusPlus20) + return; + + // Note: ignoring parens here is not justified by the standard rules, but + // ignoring parentheses seems like a more reasonable approach, and this only + // drives a deprecation warning so doesn't affect conformance. + if (auto *BO = dyn_cast(E->IgnoreParenImpCasts())) { + if (BO->getOpcode() == BO_Assign) { + auto &LHSs = ExprEvalContexts.back().VolatileAssignmentLHSs; + llvm::erase(LHSs, BO->getLHS()); + } + } +} + +void Sema::MarkExpressionAsImmediateEscalating(Expr *E) { + assert(getLangOpts().CPlusPlus20 && + ExprEvalContexts.back().InImmediateEscalatingFunctionContext && + "Cannot mark an immediate escalating expression outside of an " + "immediate escalating context"); + if (auto *Call = dyn_cast(E->IgnoreImplicit()); + Call && Call->getCallee()) { + if (auto *DeclRef = + dyn_cast(Call->getCallee()->IgnoreImplicit())) + DeclRef->setIsImmediateEscalating(true); + } else if (auto *Ctr = dyn_cast(E->IgnoreImplicit())) { + Ctr->setIsImmediateEscalating(true); + } else if (auto *DeclRef = dyn_cast(E->IgnoreImplicit())) { + DeclRef->setIsImmediateEscalating(true); + } else { + assert(false && "expected an immediately escalating expression"); + } + if (FunctionScopeInfo *FI = getCurFunction()) + FI->FoundImmediateEscalatingExpression = true; +} + +ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { + if (isUnevaluatedContext() || !E.isUsable() || !Decl || + !Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() || + isCheckingDefaultArgumentOrInitializer() || + RebuildingImmediateInvocation || isImmediateFunctionContext()) + return E; + + /// Opportunistically remove the callee from ReferencesToConsteval if we can. + /// It's OK if this fails; we'll also remove this in + /// HandleImmediateInvocations, but catching it here allows us to avoid + /// walking the AST looking for it in simple cases. + if (auto *Call = dyn_cast(E.get()->IgnoreImplicit())) + if (auto *DeclRef = + dyn_cast(Call->getCallee()->IgnoreImplicit())) + ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef); + + // C++23 [expr.const]/p16 + // An expression or conversion is immediate-escalating if it is not initially + // in an immediate function context and it is [...] an immediate invocation + // that is not a constant expression and is not a subexpression of an + // immediate invocation. + APValue Cached; + auto CheckConstantExpressionAndKeepResult = [&]() { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + bool Res = E.get()->EvaluateAsConstantExpr( + Eval, getASTContext(), ConstantExprKind::ImmediateInvocation); + if (Res && Notes.empty()) { + Cached = std::move(Eval.Val); + return true; + } + return false; + }; + + if (!E.get()->isValueDependent() && + ExprEvalContexts.back().InImmediateEscalatingFunctionContext && + !CheckConstantExpressionAndKeepResult()) { + MarkExpressionAsImmediateEscalating(E.get()); + return E; + } + + if (Cleanup.exprNeedsCleanups()) { + // Since an immediate invocation is a full expression itself - it requires + // an additional ExprWithCleanups node, but it can participate to a bigger + // full expression which actually requires cleanups to be run after so + // create ExprWithCleanups without using MaybeCreateExprWithCleanups as it + // may discard cleanups for outer expression too early. + + // Note that ExprWithCleanups created here must always have empty cleanup + // objects: + // - compound literals do not create cleanup objects in C++ and immediate + // invocations are C++-only. + // - blocks are not allowed inside constant expressions and compiler will + // issue an error if they appear there. + // + // Hence, in correct code any cleanup objects created inside current + // evaluation context must be outside the immediate invocation. + E = ExprWithCleanups::Create(getASTContext(), E.get(), + Cleanup.cleanupsHaveSideEffects(), {}); + } + + ConstantExpr *Res = ConstantExpr::Create( + getASTContext(), E.get(), + ConstantExpr::getStorageKind(Decl->getReturnType().getTypePtr(), + getASTContext()), + /*IsImmediateInvocation*/ true); + if (Cached.hasValue()) + Res->MoveIntoResult(Cached, getASTContext()); + /// Value-dependent constant expressions should not be immediately + /// evaluated until they are instantiated. + if (!Res->isValueDependent()) + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; +} + +static void EvaluateAndDiagnoseImmediateInvocation( + Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate) { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + ConstantExpr *CE = Candidate.getPointer(); + bool Result = CE->EvaluateAsConstantExpr( + Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); + if (!Result || !Notes.empty()) { + SemaRef.FailedImmediateInvocations.insert(CE); + Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); + if (auto *FunctionalCast = dyn_cast(InnerExpr)) + InnerExpr = FunctionalCast->getSubExpr()->IgnoreImplicit(); + FunctionDecl *FD = nullptr; + if (auto *Call = dyn_cast(InnerExpr)) + FD = cast(Call->getCalleeDecl()); + else if (auto *Call = dyn_cast(InnerExpr)) + FD = Call->getConstructor(); + else if (auto *Cast = dyn_cast(InnerExpr)) + FD = dyn_cast_or_null(Cast->getConversionFunction()); + + assert(FD && FD->isImmediateFunction() && + "could not find an immediate function in this expression"); + if (FD->isInvalidDecl()) + return; + SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) + << FD << FD->isConsteval(); + if (auto Context = + SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { + SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) + << Context->Decl; + SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + } + if (!FD->isConsteval()) + SemaRef.DiagnoseImmediateEscalatingReason(FD); + for (auto &Note : Notes) + SemaRef.Diag(Note.first, Note.second); + return; + } + CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); +} + +static void RemoveNestedImmediateInvocation( + Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, + SmallVector::reverse_iterator It) { + struct ComplexRemove : TreeTransform { + using Base = TreeTransform; + llvm::SmallPtrSetImpl &DRSet; + SmallVector &IISet; + SmallVector::reverse_iterator + CurrentII; + ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, + SmallVector &II, + SmallVector::reverse_iterator Current) + : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} + void RemoveImmediateInvocation(ConstantExpr* E) { + auto It = std::find_if(CurrentII, IISet.rend(), + [E](Sema::ImmediateInvocationCandidate Elem) { + return Elem.getPointer() == E; + }); + // It is possible that some subexpression of the current immediate + // invocation was handled from another expression evaluation context. Do + // not handle the current immediate invocation if some of its + // subexpressions failed before. + if (It == IISet.rend()) { + if (SemaRef.FailedImmediateInvocations.contains(E)) + CurrentII->setInt(1); + } else { + It->setInt(1); // Mark as deleted + } + } + ExprResult TransformConstantExpr(ConstantExpr *E) { + if (!E->isImmediateInvocation()) + return Base::TransformConstantExpr(E); + RemoveImmediateInvocation(E); + return Base::TransformExpr(E->getSubExpr()); + } + /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so + /// we need to remove its DeclRefExpr from the DRSet. + ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { + DRSet.erase(cast(E->getCallee()->IgnoreImplicit())); + return Base::TransformCXXOperatorCallExpr(E); + } + /// Base::TransformUserDefinedLiteral doesn't preserve the + /// UserDefinedLiteral node. + ExprResult TransformUserDefinedLiteral(UserDefinedLiteral *E) { return E; } + /// Base::TransformInitializer skips ConstantExpr so we need to visit them + /// here. + ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) { + if (!Init) + return Init; + /// ConstantExpr are the first layer of implicit node to be removed so if + /// Init isn't a ConstantExpr, no ConstantExpr will be skipped. + if (auto *CE = dyn_cast(Init)) + if (CE->isImmediateInvocation()) + RemoveImmediateInvocation(CE); + return Base::TransformInitializer(Init, NotCopyInit); + } + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return E; + } + ExprResult TransformLambdaExpr(LambdaExpr *E) { + // Do not rebuild lambdas to avoid creating a new type. + // Lambdas have already been processed inside their eval context. + return E; + } + bool AlwaysRebuild() { return false; } + bool ReplacingOriginal() { return true; } + bool AllowSkippingCXXConstructExpr() { + bool Res = AllowSkippingFirstCXXConstructExpr; + AllowSkippingFirstCXXConstructExpr = true; + return Res; + } + bool AllowSkippingFirstCXXConstructExpr = true; + } Transformer(SemaRef, Rec.ReferenceToConsteval, + Rec.ImmediateInvocationCandidates, It); + + /// CXXConstructExpr with a single argument are getting skipped by + /// TreeTransform in some situtation because they could be implicit. This + /// can only occur for the top-level CXXConstructExpr because it is used + /// nowhere in the expression being transformed therefore will not be rebuilt. + /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from + /// skipping the first CXXConstructExpr. + if (isa(It->getPointer()->IgnoreImplicit())) + Transformer.AllowSkippingFirstCXXConstructExpr = false; + + ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr()); + // The result may not be usable in case of previous compilation errors. + // In this case evaluation of the expression may result in crash so just + // don't do anything further with the result. + if (Res.isUsable()) { + Res = SemaRef.MaybeCreateExprWithCleanups(Res); + It->getPointer()->setSubExpr(Res.get()); + } +} + +static void +HandleImmediateInvocations(Sema &SemaRef, + Sema::ExpressionEvaluationContextRecord &Rec) { + if ((Rec.ImmediateInvocationCandidates.size() == 0 && + Rec.ReferenceToConsteval.size() == 0) || + Rec.isImmediateFunctionContext() || SemaRef.RebuildingImmediateInvocation) + return; + + /// When we have more than 1 ImmediateInvocationCandidates or previously + /// failed immediate invocations, we need to check for nested + /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. + /// Otherwise we only need to remove ReferenceToConsteval in the immediate + /// invocation. + if (Rec.ImmediateInvocationCandidates.size() > 1 || + !SemaRef.FailedImmediateInvocations.empty()) { + + /// Prevent sema calls during the tree transform from adding pointers that + /// are already in the sets. + llvm::SaveAndRestore DisableIITracking( + SemaRef.RebuildingImmediateInvocation, true); + + /// Prevent diagnostic during tree transfrom as they are duplicates + Sema::TentativeAnalysisScope DisableDiag(SemaRef); + + for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); + It != Rec.ImmediateInvocationCandidates.rend(); It++) + if (!It->getInt()) + RemoveNestedImmediateInvocation(SemaRef, Rec, It); + } else if (Rec.ImmediateInvocationCandidates.size() == 1 && + Rec.ReferenceToConsteval.size()) { + struct SimpleRemove : RecursiveASTVisitor { + llvm::SmallPtrSetImpl &DRSet; + SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} + bool VisitDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return DRSet.size(); + } + } Visitor(Rec.ReferenceToConsteval); + Visitor.TraverseStmt( + Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); + } + for (auto CE : Rec.ImmediateInvocationCandidates) + if (!CE.getInt()) + EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + for (auto *DR : Rec.ReferenceToConsteval) { + // If the expression is immediate escalating, it is not an error; + // The outer context itself becomes immediate and further errors, + // if any, will be handled by DiagnoseImmediateEscalatingReason. + if (DR->isImmediateEscalating()) + continue; + auto *FD = cast(DR->getDecl()); + const NamedDecl *ND = FD; + if (const auto *MD = dyn_cast(ND); + MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD))) + ND = MD->getParent(); + + // C++23 [expr.const]/p16 + // An expression or conversion is immediate-escalating if it is not + // initially in an immediate function context and it is [...] a + // potentially-evaluated id-expression that denotes an immediate function + // that is not a subexpression of an immediate invocation. + bool ImmediateEscalating = false; + bool IsPotentiallyEvaluated = + Rec.Context == + Sema::ExpressionEvaluationContext::PotentiallyEvaluated || + Rec.Context == + Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; + if (SemaRef.inTemplateInstantiation() && IsPotentiallyEvaluated) + ImmediateEscalating = Rec.InImmediateEscalatingFunctionContext; + + if (!Rec.InImmediateEscalatingFunctionContext || + (SemaRef.inTemplateInstantiation() && !ImmediateEscalating)) { + SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) + << ND << isa(ND) << FD->isConsteval(); + SemaRef.Diag(ND->getLocation(), diag::note_declared_at); + if (auto Context = + SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { + SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) + << Context->Decl; + SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + } + if (FD->isImmediateEscalating() && !FD->isConsteval()) + SemaRef.DiagnoseImmediateEscalatingReason(FD); + + } else { + SemaRef.MarkExpressionAsImmediateEscalating(DR); + } + } +} + +void Sema::PopExpressionEvaluationContext() { + ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); + unsigned NumTypos = Rec.NumTypos; + + if (!Rec.Lambdas.empty()) { + using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; + if (!getLangOpts().CPlusPlus20 && + (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || + Rec.isUnevaluated() || + (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17))) { + unsigned D; + if (Rec.isUnevaluated()) { + // C++11 [expr.prim.lambda]p2: + // A lambda-expression shall not appear in an unevaluated operand + // (Clause 5). + D = diag::err_lambda_unevaluated_operand; + } else if (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17) { + // C++1y [expr.const]p2: + // A conditional-expression e is a core constant expression unless the + // evaluation of e, following the rules of the abstract machine, would + // evaluate [...] a lambda-expression. + D = diag::err_lambda_in_constant_expression; + } else if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument) { + // C++17 [expr.prim.lamda]p2: + // A lambda-expression shall not appear [...] in a template-argument. + D = diag::err_lambda_in_invalid_context; + } else + llvm_unreachable("Couldn't infer lambda error message."); + + for (const auto *L : Rec.Lambdas) + Diag(L->getBeginLoc(), D); + } + } - Converted = PerformContextualImplicitConversion(DiagLoc, E, - ConvertDiagnoser); - if (Converted.isInvalid()) - return Converted; - E = Converted.get(); - // The 'explicit' case causes us to get a RecoveryExpr. Give up here so we - // don't try to evaluate it later. We also don't want to return the - // RecoveryExpr here, as it results in this call succeeding, thus callers of - // this function will attempt to use 'Value'. - if (isa(E)) - return ExprError(); - if (!E->getType()->isIntegralOrUnscopedEnumerationType()) - return ExprError(); - } else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) { - // An ICE must be of integral or unscoped enumeration type. - if (!Diagnoser.Suppress) - Diagnoser.diagnoseNotICEType(*this, DiagLoc, E->getType()) - << E->getSourceRange(); - return ExprError(); + // Append the collected materialized temporaries into previous context before + // exit if the previous also is a lifetime extending context. + auto &PrevRecord = parentEvaluationContext(); + if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && + PrevRecord.InLifetimeExtendingContext && + !Rec.ForRangeLifetimeExtendTemps.empty()) { + PrevRecord.ForRangeLifetimeExtendTemps.append( + Rec.ForRangeLifetimeExtendTemps); } - ExprResult RValueExpr = DefaultLvalueConversion(E); - if (RValueExpr.isInvalid()) + WarnOnPendingNoDerefs(Rec); + HandleImmediateInvocations(*this, Rec); + + // Warn on any volatile-qualified simple-assignments that are not discarded- + // value expressions nor unevaluated operands (those cases get removed from + // this list by CheckUnusedVolatileAssignment). + for (auto *BO : Rec.VolatileAssignmentLHSs) + Diag(BO->getBeginLoc(), diag::warn_deprecated_simple_assign_volatile) + << BO->getType(); + + // When are coming out of an unevaluated context, clear out any + // temporaries that we may have created as part of the evaluation of + // the expression in that context: they aren't relevant because they + // will never be constructed. + if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { + ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, + ExprCleanupObjects.end()); + Cleanup = Rec.ParentCleanup; + CleanupVarDeclMarking(); + std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); + // Otherwise, merge the contexts together. + } else { + Cleanup.mergeFrom(Rec.ParentCleanup); + MaybeODRUseExprs.insert(Rec.SavedMaybeODRUseExprs.begin(), + Rec.SavedMaybeODRUseExprs.end()); + } + + // Pop the current expression evaluation context off the stack. + ExprEvalContexts.pop_back(); + + // The global expression evaluation context record is never popped. + ExprEvalContexts.back().NumTypos += NumTypos; +} + +void Sema::DiscardCleanupsInEvaluationContext() { + ExprCleanupObjects.erase( + ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, + ExprCleanupObjects.end()); + Cleanup.reset(); + MaybeODRUseExprs.clear(); +} + +ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) { + ExprResult Result = CheckPlaceholderExpr(E); + if (Result.isInvalid()) return ExprError(); + E = Result.get(); + if (!E->getType()->isVariablyModifiedType()) + return E; + return TransformToPotentiallyEvaluated(E); +} - E = RValueExpr.get(); +/// Are we in a context that is potentially constant evaluated per C++20 +/// [expr.const]p12? +static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) { + /// C++2a [expr.const]p12: + // An expression or conversion is potentially constant evaluated if it is + switch (SemaRef.ExprEvalContexts.back().Context) { + case Sema::ExpressionEvaluationContext::ConstantEvaluated: + case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - // Circumvent ICE checking in C++11 to avoid evaluating the expression twice - // in the non-ICE case. - if (!getLangOpts().CPlusPlus11 && E->isIntegerConstantExpr(Context)) { - SmallVector Notes; - if (Result) - *Result = E->EvaluateKnownConstIntCheckOverflow(Context, &Notes); - if (!isa(E)) - E = Result ? ConstantExpr::Create(Context, E, APValue(*Result)) - : ConstantExpr::Create(Context, E); - - if (Notes.empty()) - return E; - - // If our only note is the usual "invalid subexpression" note, just point - // the caret at its location rather than producing an essentially - // redundant note. - if (Notes.size() == 1 && Notes[0].second.getDiagID() == - diag::note_invalid_subexpr_in_const_expr) { - DiagLoc = Notes[0].first; - Notes.clear(); - } - - if (getLangOpts().CPlusPlus) { - if (!Diagnoser.Suppress) { - Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); + // -- a manifestly constant-evaluated expression, + case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: + case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + case Sema::ExpressionEvaluationContext::DiscardedStatement: + // -- a potentially-evaluated expression, + case Sema::ExpressionEvaluationContext::UnevaluatedList: + // -- an immediate subexpression of a braced-init-list, + + // -- [FIXME] an expression of the form & cast-expression that occurs + // within a templated entity + // -- a subexpression of one of the above that is not a subexpression of + // a nested unevaluated operand. + return true; + + case Sema::ExpressionEvaluationContext::Unevaluated: + case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + // Expressions in this context are never evaluated. + return false; + } + llvm_unreachable("Invalid context"); +} + +/// Return true if this function has a calling convention that requires mangling +/// in the size of the parameter pack. +static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) { + // These manglings don't do anything on non-Windows or non-x86 platforms, so + // we don't need parameter type sizes. + const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); + if (!TT.isOSWindows() || !TT.isX86()) + return false; + + // If this is C++ and this isn't an extern "C" function, parameters do not + // need to be complete. In this case, C++ mangling will apply, which doesn't + // use the size of the parameters. + if (S.getLangOpts().CPlusPlus && !FD->isExternC()) + return false; + + // Stdcall, fastcall, and vectorcall need this special treatment. + CallingConv CC = FD->getType()->castAs()->getCallConv(); + switch (CC) { + case CC_X86StdCall: + case CC_X86FastCall: + case CC_X86VectorCall: + return true; + default: + break; + } + return false; +} + +/// Require that all of the parameter types of function be complete. Normally, +/// parameter types are only required to be complete when a function is called +/// or defined, but to mangle functions with certain calling conventions, the +/// mangler needs to know the size of the parameter list. In this situation, +/// MSVC doesn't emit an error or instantiate templates. Instead, MSVC mangles +/// the function as _foo@0, i.e. zero bytes of parameters, which will usually +/// result in a linker error. Clang doesn't implement this behavior, and instead +/// attempts to error at compile time. +static void CheckCompleteParameterTypesForMangler(Sema &S, FunctionDecl *FD, + SourceLocation Loc) { + class ParamIncompleteTypeDiagnoser : public Sema::TypeDiagnoser { + FunctionDecl *FD; + ParmVarDecl *Param; + + public: + ParamIncompleteTypeDiagnoser(FunctionDecl *FD, ParmVarDecl *Param) + : FD(FD), Param(Param) {} + + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + CallingConv CC = FD->getType()->castAs()->getCallConv(); + StringRef CCName; + switch (CC) { + case CC_X86StdCall: + CCName = "stdcall"; + break; + case CC_X86FastCall: + CCName = "fastcall"; + break; + case CC_X86VectorCall: + CCName = "vectorcall"; + break; + default: + llvm_unreachable("CC does not need mangling"); } - return ExprError(); + + S.Diag(Loc, diag::err_cconv_incomplete_param_type) + << Param->getDeclName() << FD->getDeclName() << CCName; } + }; + + for (ParmVarDecl *Param : FD->parameters()) { + ParamIncompleteTypeDiagnoser Diagnoser(FD, Param); + S.RequireCompleteType(Loc, Param->getType(), Diagnoser); + } +} + +namespace { +enum class OdrUseContext { + /// Declarations in this context are not odr-used. + None, + /// Declarations in this context are formally odr-used, but this is a + /// dependent context. + Dependent, + /// Declarations in this context are odr-used but not actually used (yet). + FormallyOdrUsed, + /// Declarations in this context are used. + Used +}; +} + +/// Are we within a context in which references to resolved functions or to +/// variables result in odr-use? +static OdrUseContext isOdrUseContext(Sema &SemaRef) { + OdrUseContext Result; + + switch (SemaRef.ExprEvalContexts.back().Context) { + case Sema::ExpressionEvaluationContext::Unevaluated: + case Sema::ExpressionEvaluationContext::UnevaluatedList: + case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + return OdrUseContext::None; - Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); - - return E; + case Sema::ExpressionEvaluationContext::ConstantEvaluated: + case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: + case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: + Result = OdrUseContext::Used; + break; + + case Sema::ExpressionEvaluationContext::DiscardedStatement: + Result = OdrUseContext::FormallyOdrUsed; + break; + + case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + // A default argument formally results in odr-use, but doesn't actually + // result in a use in any real sense until it itself is used. + Result = OdrUseContext::FormallyOdrUsed; + break; } - Expr::EvalResult EvalResult; - SmallVector Notes; - EvalResult.Diag = &Notes; + if (SemaRef.CurContext->isDependentContext()) + return OdrUseContext::Dependent; - // Try to evaluate the expression, and produce diagnostics explaining why it's - // not a constant expression as a side-effect. - bool Folded = - E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) && - EvalResult.Val.isInt() && !EvalResult.HasSideEffects && - (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); + return Result; +} - if (!isa(E)) - E = ConstantExpr::Create(Context, E, EvalResult.Val); +static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { + if (!Func->isConstexpr()) + return false; - // In C++11, we can rely on diagnostics being produced for any expression - // which is not a constant expression. If no diagnostics were produced, then - // this is a constant expression. - if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) { - if (Result) - *Result = EvalResult.Val.getInt(); - return E; - } + if (Func->isImplicitlyInstantiable() || !Func->isUserProvided()) + return true; + auto *CCD = dyn_cast(Func); + return CCD && CCD->getInheritedConstructor(); +} - // If our only note is the usual "invalid subexpression" note, just point - // the caret at its location rather than producing an essentially - // redundant note. - if (Notes.size() == 1 && Notes[0].second.getDiagID() == - diag::note_invalid_subexpr_in_const_expr) { - DiagLoc = Notes[0].first; - Notes.clear(); - } +void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, + bool MightBeOdrUse) { + assert(Func && "No function?"); - if (!Folded || !CanFold) { - if (!Diagnoser.Suppress) { - Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); - } + Func->setReferenced(); - return ExprError(); + // Recursive functions aren't really used until they're used from some other + // context. + bool IsRecursiveCall = CurContext == Func; + + // C++11 [basic.def.odr]p3: + // A function whose name appears as a potentially-evaluated expression is + // odr-used if it is the unique lookup result or the selected member of a + // set of overloaded functions [...]. + // + // We (incorrectly) mark overload resolution as an unevaluated context, so we + // can just check that here. + OdrUseContext OdrUse = + MightBeOdrUse ? isOdrUseContext(*this) : OdrUseContext::None; + if (IsRecursiveCall && OdrUse == OdrUseContext::Used) + OdrUse = OdrUseContext::FormallyOdrUsed; + + // Trivial default constructors and destructors are never actually used. + // FIXME: What about other special members? + if (Func->isTrivial() && !Func->hasAttr() && + OdrUse == OdrUseContext::Used) { + if (auto *Constructor = dyn_cast(Func)) + if (Constructor->isDefaultConstructor()) + OdrUse = OdrUseContext::FormallyOdrUsed; + if (isa(Func)) + OdrUse = OdrUseContext::FormallyOdrUsed; } - Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); + // C++20 [expr.const]p12: + // A function [...] is needed for constant evaluation if it is [...] a + // constexpr function that is named by an expression that is potentially + // constant evaluated + bool NeededForConstantEvaluation = + isPotentiallyConstantEvaluatedContext(*this) && + isImplicitlyDefinableConstexprFunction(Func); - if (Result) - *Result = EvalResult.Val.getInt(); - return E; -} + // Determine whether we require a function definition to exist, per + // C++11 [temp.inst]p3: + // Unless a function template specialization has been explicitly + // instantiated or explicitly specialized, the function template + // specialization is implicitly instantiated when the specialization is + // referenced in a context that requires a function definition to exist. + // C++20 [temp.inst]p7: + // The existence of a definition of a [...] function is considered to + // affect the semantics of the program if the [...] function is needed for + // constant evaluation by an expression + // C++20 [basic.def.odr]p10: + // Every program shall contain exactly one definition of every non-inline + // function or variable that is odr-used in that program outside of a + // discarded statement + // C++20 [special]p1: + // The implementation will implicitly define [defaulted special members] + // if they are odr-used or needed for constant evaluation. + // + // Note that we skip the implicit instantiation of templates that are only + // used in unused default arguments or by recursive calls to themselves. + // This is formally non-conforming, but seems reasonable in practice. + bool NeedDefinition = + !IsRecursiveCall && + (OdrUse == OdrUseContext::Used || + (NeededForConstantEvaluation && !Func->isPureVirtual())); -namespace { - // Handle the case where we conclude a expression which we speculatively - // considered to be unevaluated is actually evaluated. - class TransformToPE : public TreeTransform { - typedef TreeTransform BaseTransform; + // C++14 [temp.expl.spec]p6: + // If a template [...] is explicitly specialized then that specialization + // shall be declared before the first use of that specialization that would + // cause an implicit instantiation to take place, in every translation unit + // in which such a use occurs + if (NeedDefinition && + (Func->getTemplateSpecializationKind() != TSK_Undeclared || + Func->getMemberSpecializationInfo())) + checkSpecializationReachability(Loc, Func); - public: - TransformToPE(Sema &SemaRef) : BaseTransform(SemaRef) { } + if (getLangOpts().CUDA) + CUDA().CheckCall(Loc, Func); - // Make sure we redo semantic analysis - bool AlwaysRebuild() { return true; } - bool ReplacingOriginal() { return true; } + // If we need a definition, try to create one. + if (NeedDefinition && !Func->getBody()) { + runWithSufficientStackSpace(Loc, [&] { + if (CXXConstructorDecl *Constructor = + dyn_cast(Func)) { + Constructor = cast(Constructor->getFirstDecl()); + if (Constructor->isDefaulted() && !Constructor->isDeleted()) { + if (Constructor->isDefaultConstructor()) { + if (Constructor->isTrivial() && + !Constructor->hasAttr()) + return; + DefineImplicitDefaultConstructor(Loc, Constructor); + } else if (Constructor->isCopyConstructor()) { + DefineImplicitCopyConstructor(Loc, Constructor); + } else if (Constructor->isMoveConstructor()) { + DefineImplicitMoveConstructor(Loc, Constructor); + } + } else if (Constructor->getInheritedConstructor()) { + DefineInheritingConstructor(Loc, Constructor); + } + } else if (CXXDestructorDecl *Destructor = + dyn_cast(Func)) { + Destructor = cast(Destructor->getFirstDecl()); + if (Destructor->isDefaulted() && !Destructor->isDeleted()) { + if (Destructor->isTrivial() && !Destructor->hasAttr()) + return; + DefineImplicitDestructor(Loc, Destructor); + } + if (Destructor->isVirtual() && getLangOpts().AppleKext) + MarkVTableUsed(Loc, Destructor->getParent()); + } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { + if (MethodDecl->isOverloadedOperator() && + MethodDecl->getOverloadedOperator() == OO_Equal) { + MethodDecl = cast(MethodDecl->getFirstDecl()); + if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { + if (MethodDecl->isCopyAssignmentOperator()) + DefineImplicitCopyAssignment(Loc, MethodDecl); + else if (MethodDecl->isMoveAssignmentOperator()) + DefineImplicitMoveAssignment(Loc, MethodDecl); + } + } else if (isa(MethodDecl) && + MethodDecl->getParent()->isLambda()) { + CXXConversionDecl *Conversion = + cast(MethodDecl->getFirstDecl()); + if (Conversion->isLambdaToBlockPointerConversion()) + DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); + else + DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); + } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) + MarkVTableUsed(Loc, MethodDecl->getParent()); + } - // We need to special-case DeclRefExprs referring to FieldDecls which - // are not part of a member pointer formation; normal TreeTransforming - // doesn't catch this case because of the way we represent them in the AST. - // FIXME: This is a bit ugly; is it really the best way to handle this - // case? - // - // Error on DeclRefExprs referring to FieldDecls. - ExprResult TransformDeclRefExpr(DeclRefExpr *E) { - if (isa(E->getDecl()) && - !SemaRef.isUnevaluatedContext()) - return SemaRef.Diag(E->getLocation(), - diag::err_invalid_non_static_member_use) - << E->getDecl() << E->getSourceRange(); + if (Func->isDefaulted() && !Func->isDeleted()) { + DefaultedComparisonKind DCK = getDefaultedComparisonKind(Func); + if (DCK != DefaultedComparisonKind::None) + DefineDefaultedComparison(Loc, Func, DCK); + } + + // Implicit instantiation of function templates and member functions of + // class templates. + if (Func->isImplicitlyInstantiable()) { + TemplateSpecializationKind TSK = + Func->getTemplateSpecializationKindForInstantiation(); + SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); + bool FirstInstantiation = PointOfInstantiation.isInvalid(); + if (FirstInstantiation) { + PointOfInstantiation = Loc; + if (auto *MSI = Func->getMemberSpecializationInfo()) + MSI->setPointOfInstantiation(Loc); + // FIXME: Notify listener. + else + Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); + } else if (TSK != TSK_ImplicitInstantiation) { + // Use the point of use as the point of instantiation, instead of the + // point of explicit instantiation (which we track as the actual point + // of instantiation). This gives better backtraces in diagnostics. + PointOfInstantiation = Loc; + } + + if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || + Func->isConstexpr()) { + if (isa(Func->getDeclContext()) && + cast(Func->getDeclContext())->isLocalClass() && + CodeSynthesisContexts.size()) + PendingLocalImplicitInstantiations.push_back( + std::make_pair(Func, PointOfInstantiation)); + else if (Func->isConstexpr()) + // Do not defer instantiations of constexpr functions, to avoid the + // expression evaluator needing to call back into Sema if it sees a + // call to such a function. + InstantiateFunctionDefinition(PointOfInstantiation, Func); + else { + Func->setInstantiationIsPending(true); + PendingInstantiations.push_back( + std::make_pair(Func, PointOfInstantiation)); + // Notify the consumer that a function was implicitly instantiated. + Consumer.HandleCXXImplicitFunctionInstantiation(Func); + } + } + } else { + // Walk redefinitions, as some of them may be instantiable. + for (auto *i : Func->redecls()) { + if (!i->isUsed(false) && i->isImplicitlyInstantiable()) + MarkFunctionReferenced(Loc, i, MightBeOdrUse); + } + } + }); + } - return BaseTransform::TransformDeclRefExpr(E); + // If a constructor was defined in the context of a default parameter + // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed + // context), its initializers may not be referenced yet. + if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { + EnterExpressionEvaluationContext EvalContext( + *this, + Constructor->isImmediateFunction() + ? ExpressionEvaluationContext::ImmediateFunctionContext + : ExpressionEvaluationContext::PotentiallyEvaluated, + Constructor); + for (CXXCtorInitializer *Init : Constructor->inits()) { + if (Init->isInClassMemberInitializer()) + runWithSufficientStackSpace(Init->getSourceLocation(), [&]() { + MarkDeclarationsReferencedInExpr(Init->getInit()); + }); } + } - // Exception: filter out member pointer formation - ExprResult TransformUnaryOperator(UnaryOperator *E) { - if (E->getOpcode() == UO_AddrOf && E->getType()->isMemberPointerType()) - return E; + // C++14 [except.spec]p17: + // An exception-specification is considered to be needed when: + // - the function is odr-used or, if it appears in an unevaluated operand, + // would be odr-used if the expression were potentially-evaluated; + // + // Note, we do this even if MightBeOdrUse is false. That indicates that the + // function is a pure virtual function we're calling, and in that case the + // function was selected by overload resolution and we need to resolve its + // exception specification for a different reason. + const FunctionProtoType *FPT = Func->getType()->getAs(); + if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) + ResolveExceptionSpec(Loc, FPT); - return BaseTransform::TransformUnaryOperator(E); - } + // A callee could be called by a host function then by a device function. + // If we only try recording once, we will miss recording the use on device + // side. Therefore keep trying until it is recorded. + if (LangOpts.OffloadImplicitHostDeviceTemplates && LangOpts.CUDAIsDevice && + !getASTContext().CUDAImplicitHostDeviceFunUsedByDevice.count(Func)) + CUDA().RecordImplicitHostDeviceFuncUsedByDevice(Func); - // The body of a lambda-expression is in a separate expression evaluation - // context so never needs to be transformed. - // FIXME: Ideally we wouldn't transform the closure type either, and would - // just recreate the capture expressions and lambda expression. - StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) { - return SkipLambdaBody(E, Body); + // If this is the first "real" use, act on that. + if (OdrUse == OdrUseContext::Used && !Func->isUsed(/*CheckUsedAttr=*/false)) { + // Keep track of used but undefined functions. + if (!Func->isDefined()) { + if (mightHaveNonExternalLinkage(Func)) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + else if (Func->getMostRecentDecl()->isInlined() && + !LangOpts.GNUInline && + !Func->getMostRecentDecl()->hasAttr()) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + else if (isExternalWithNoLinkageType(Func)) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); } - }; -} - -ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) { - assert(isUnevaluatedContext() && - "Should only transform unevaluated expressions"); - ExprEvalContexts.back().Context = - ExprEvalContexts[ExprEvalContexts.size()-2].Context; - if (isUnevaluatedContext()) - return E; - return TransformToPE(*this).TransformExpr(E); -} - -TypeSourceInfo *Sema::TransformToPotentiallyEvaluated(TypeSourceInfo *TInfo) { - assert(isUnevaluatedContext() && - "Should only transform unevaluated expressions"); - ExprEvalContexts.back().Context = parentEvaluationContext().Context; - if (isUnevaluatedContext()) - return TInfo; - return TransformToPE(*this).TransformType(TInfo); -} - -void -Sema::PushExpressionEvaluationContext( - ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, - ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { - ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, - LambdaContextDecl, ExprContext); - - // Discarded statements and immediate contexts nested in other - // discarded statements or immediate context are themselves - // a discarded statement or an immediate context, respectively. - ExprEvalContexts.back().InDiscardedStatement = - parentEvaluationContext().isDiscardedStatementContext(); - - // C++23 [expr.const]/p15 - // An expression or conversion is in an immediate function context if [...] - // it is a subexpression of a manifestly constant-evaluated expression or - // conversion. - const auto &Prev = parentEvaluationContext(); - ExprEvalContexts.back().InImmediateFunctionContext = - Prev.isImmediateFunctionContext() || Prev.isConstantEvaluated(); - ExprEvalContexts.back().InImmediateEscalatingFunctionContext = - Prev.InImmediateEscalatingFunctionContext; + // Some x86 Windows calling conventions mangle the size of the parameter + // pack into the name. Computing the size of the parameters requires the + // parameter types to be complete. Check that now. + if (funcHasParameterSizeMangling(*this, Func)) + CheckCompleteParameterTypesForMangler(*this, Func, Loc); - Cleanup.reset(); - if (!MaybeODRUseExprs.empty()) - std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); -} + // In the MS C++ ABI, the compiler emits destructor variants where they are + // used. If the destructor is used here but defined elsewhere, mark the + // virtual base destructors referenced. If those virtual base destructors + // are inline, this will ensure they are defined when emitting the complete + // destructor variant. This checking may be redundant if the destructor is + // provided later in this TU. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { + if (auto *Dtor = dyn_cast(Func)) { + CXXRecordDecl *Parent = Dtor->getParent(); + if (Parent->getNumVBases() > 0 && !Dtor->getBody()) + CheckCompleteDestructorVariant(Loc, Dtor); + } + } -void -Sema::PushExpressionEvaluationContext( - ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t, - ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { - Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl; - PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); + Func->markUsed(Context); + } } -namespace { - -const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { - PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); - if (const auto *E = dyn_cast(PossibleDeref)) { - if (E->getOpcode() == UO_Deref) - return CheckPossibleDeref(S, E->getSubExpr()); - } else if (const auto *E = dyn_cast(PossibleDeref)) { - return CheckPossibleDeref(S, E->getBase()); - } else if (const auto *E = dyn_cast(PossibleDeref)) { - return CheckPossibleDeref(S, E->getBase()); - } else if (const auto E = dyn_cast(PossibleDeref)) { - QualType Inner; - QualType Ty = E->getType(); - if (const auto *Ptr = Ty->getAs()) - Inner = Ptr->getPointeeType(); - else if (const auto *Arr = S.Context.getAsArrayType(Ty)) - Inner = Arr->getElementType(); - else - return nullptr; +/// Directly mark a variable odr-used. Given a choice, prefer to use +/// MarkVariableReferenced since it does additional checks and then +/// calls MarkVarDeclODRUsed. +/// If the variable must be captured: +/// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext +/// - else capture it in the DeclContext that maps to the +/// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack. +static void +MarkVarDeclODRUsed(ValueDecl *V, SourceLocation Loc, Sema &SemaRef, + const unsigned *const FunctionScopeIndexToStopAt = nullptr) { + // Keep track of used but undefined variables. + // FIXME: We shouldn't suppress this warning for static data members. + VarDecl *Var = V->getPotentiallyDecomposedVarDecl(); + assert(Var && "expected a capturable variable"); - if (Inner->hasAttr(attr::NoDeref)) - return E; + if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && + (!Var->isExternallyVisible() || Var->isInline() || + SemaRef.isExternalWithNoLinkageType(Var)) && + !(Var->isStaticDataMember() && Var->hasInit())) { + SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; + if (old.isInvalid()) + old = Loc; } - return nullptr; -} - -} // namespace + QualType CaptureType, DeclRefType; + if (SemaRef.LangOpts.OpenMP) + SemaRef.OpenMP().tryCaptureOpenMPLambdas(V); + SemaRef.tryCaptureVariable(V, Loc, Sema::TryCapture_Implicit, + /*EllipsisLoc*/ SourceLocation(), + /*BuildAndDiagnose*/ true, CaptureType, + DeclRefType, FunctionScopeIndexToStopAt); -void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { - for (const Expr *E : Rec.PossibleDerefs) { - const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); - if (DeclRef) { - const ValueDecl *Decl = DeclRef->getDecl(); - Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) - << Decl->getName() << E->getSourceRange(); - Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); - } else { - Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) - << E->getSourceRange(); + if (SemaRef.LangOpts.CUDA && Var->hasGlobalStorage()) { + auto *FD = dyn_cast_or_null(SemaRef.CurContext); + auto VarTarget = SemaRef.CUDA().IdentifyTarget(Var); + auto UserTarget = SemaRef.CUDA().IdentifyTarget(FD); + if (VarTarget == SemaCUDA::CVT_Host && + (UserTarget == CUDAFunctionTarget::Device || + UserTarget == CUDAFunctionTarget::HostDevice || + UserTarget == CUDAFunctionTarget::Global)) { + // Diagnose ODR-use of host global variables in device functions. + // Reference of device global variables in host functions is allowed + // through shadow variables therefore it is not diagnosed. + if (SemaRef.LangOpts.CUDAIsDevice && !SemaRef.LangOpts.HIPStdPar) { + SemaRef.targetDiag(Loc, diag::err_ref_bad_target) + << /*host*/ 2 << /*variable*/ 1 << Var + << llvm::to_underlying(UserTarget); + SemaRef.targetDiag(Var->getLocation(), + Var->getType().isConstQualified() + ? diag::note_cuda_const_var_unpromoted + : diag::note_cuda_host_var); + } + } else if (VarTarget == SemaCUDA::CVT_Device && + !Var->hasAttr() && + (UserTarget == CUDAFunctionTarget::Host || + UserTarget == CUDAFunctionTarget::HostDevice)) { + // Record a CUDA/HIP device side variable if it is ODR-used + // by host code. This is done conservatively, when the variable is + // referenced in any of the following contexts: + // - a non-function context + // - a host function + // - a host device function + // This makes the ODR-use of the device side variable by host code to + // be visible in the device compilation for the compiler to be able to + // emit template variables instantiated by host code only and to + // externalize the static device side variable ODR-used by host code. + if (!Var->hasExternalStorage()) + SemaRef.getASTContext().CUDADeviceVarODRUsedByHost.insert(Var); + else if (SemaRef.LangOpts.GPURelocatableDeviceCode && + (!FD || (!FD->getDescribedFunctionTemplate() && + SemaRef.getASTContext().GetGVALinkageForFunction(FD) == + GVA_StrongExternal))) + SemaRef.getASTContext().CUDAExternalDeviceDeclODRUsedByHost.insert(Var); } } - Rec.PossibleDerefs.clear(); -} - -void Sema::CheckUnusedVolatileAssignment(Expr *E) { - if (!E->getType().isVolatileQualified() || !getLangOpts().CPlusPlus20) - return; - // Note: ignoring parens here is not justified by the standard rules, but - // ignoring parentheses seems like a more reasonable approach, and this only - // drives a deprecation warning so doesn't affect conformance. - if (auto *BO = dyn_cast(E->IgnoreParenImpCasts())) { - if (BO->getOpcode() == BO_Assign) { - auto &LHSs = ExprEvalContexts.back().VolatileAssignmentLHSs; - llvm::erase(LHSs, BO->getLHS()); - } - } + V->markUsed(SemaRef.Context); } -void Sema::MarkExpressionAsImmediateEscalating(Expr *E) { - assert(getLangOpts().CPlusPlus20 && - ExprEvalContexts.back().InImmediateEscalatingFunctionContext && - "Cannot mark an immediate escalating expression outside of an " - "immediate escalating context"); - if (auto *Call = dyn_cast(E->IgnoreImplicit()); - Call && Call->getCallee()) { - if (auto *DeclRef = - dyn_cast(Call->getCallee()->IgnoreImplicit())) - DeclRef->setIsImmediateEscalating(true); - } else if (auto *Ctr = dyn_cast(E->IgnoreImplicit())) { - Ctr->setIsImmediateEscalating(true); - } else if (auto *DeclRef = dyn_cast(E->IgnoreImplicit())) { - DeclRef->setIsImmediateEscalating(true); - } else { - assert(false && "expected an immediately escalating expression"); - } - if (FunctionScopeInfo *FI = getCurFunction()) - FI->FoundImmediateEscalatingExpression = true; +void Sema::MarkCaptureUsedInEnclosingContext(ValueDecl *Capture, + SourceLocation Loc, + unsigned CapturingScopeIndex) { + MarkVarDeclODRUsed(Capture, Loc, *this, &CapturingScopeIndex); } -ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { - if (isUnevaluatedContext() || !E.isUsable() || !Decl || - !Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() || - isCheckingDefaultArgumentOrInitializer() || - RebuildingImmediateInvocation || isImmediateFunctionContext()) - return E; - - /// Opportunistically remove the callee from ReferencesToConsteval if we can. - /// It's OK if this fails; we'll also remove this in - /// HandleImmediateInvocations, but catching it here allows us to avoid - /// walking the AST looking for it in simple cases. - if (auto *Call = dyn_cast(E.get()->IgnoreImplicit())) - if (auto *DeclRef = - dyn_cast(Call->getCallee()->IgnoreImplicit())) - ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef); - - // C++23 [expr.const]/p16 - // An expression or conversion is immediate-escalating if it is not initially - // in an immediate function context and it is [...] an immediate invocation - // that is not a constant expression and is not a subexpression of an - // immediate invocation. - APValue Cached; - auto CheckConstantExpressionAndKeepResult = [&]() { - llvm::SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - bool Res = E.get()->EvaluateAsConstantExpr( - Eval, getASTContext(), ConstantExprKind::ImmediateInvocation); - if (Res && Notes.empty()) { - Cached = std::move(Eval.Val); - return true; - } - return false; - }; - - if (!E.get()->isValueDependent() && - ExprEvalContexts.back().InImmediateEscalatingFunctionContext && - !CheckConstantExpressionAndKeepResult()) { - MarkExpressionAsImmediateEscalating(E.get()); - return E; - } - - if (Cleanup.exprNeedsCleanups()) { - // Since an immediate invocation is a full expression itself - it requires - // an additional ExprWithCleanups node, but it can participate to a bigger - // full expression which actually requires cleanups to be run after so - // create ExprWithCleanups without using MaybeCreateExprWithCleanups as it - // may discard cleanups for outer expression too early. - - // Note that ExprWithCleanups created here must always have empty cleanup - // objects: - // - compound literals do not create cleanup objects in C++ and immediate - // invocations are C++-only. - // - blocks are not allowed inside constant expressions and compiler will - // issue an error if they appear there. - // - // Hence, in correct code any cleanup objects created inside current - // evaluation context must be outside the immediate invocation. - E = ExprWithCleanups::Create(getASTContext(), E.get(), - Cleanup.cleanupsHaveSideEffects(), {}); - } +void diagnoseUncapturableValueReferenceOrBinding(Sema &S, SourceLocation loc, + ValueDecl *var) { + DeclContext *VarDC = var->getDeclContext(); - ConstantExpr *Res = ConstantExpr::Create( - getASTContext(), E.get(), - ConstantExpr::getStorageKind(Decl->getReturnType().getTypePtr(), - getASTContext()), - /*IsImmediateInvocation*/ true); - if (Cached.hasValue()) - Res->MoveIntoResult(Cached, getASTContext()); - /// Value-dependent constant expressions should not be immediately - /// evaluated until they are instantiated. - if (!Res->isValueDependent()) - ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); - return Res; -} + // If the parameter still belongs to the translation unit, then + // we're actually just using one parameter in the declaration of + // the next. + if (isa(var) && + isa(VarDC)) + return; -static void EvaluateAndDiagnoseImmediateInvocation( - Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate) { - llvm::SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - ConstantExpr *CE = Candidate.getPointer(); - bool Result = CE->EvaluateAsConstantExpr( - Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); - if (!Result || !Notes.empty()) { - SemaRef.FailedImmediateInvocations.insert(CE); - Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); - if (auto *FunctionalCast = dyn_cast(InnerExpr)) - InnerExpr = FunctionalCast->getSubExpr()->IgnoreImplicit(); - FunctionDecl *FD = nullptr; - if (auto *Call = dyn_cast(InnerExpr)) - FD = cast(Call->getCalleeDecl()); - else if (auto *Call = dyn_cast(InnerExpr)) - FD = Call->getConstructor(); - else if (auto *Cast = dyn_cast(InnerExpr)) - FD = dyn_cast_or_null(Cast->getConversionFunction()); + // For C code, don't diagnose about capture if we're not actually in code + // right now; it's impossible to write a non-constant expression outside of + // function context, so we'll get other (more useful) diagnostics later. + // + // For C++, things get a bit more nasty... it would be nice to suppress this + // diagnostic for certain cases like using a local variable in an array bound + // for a member of a local class, but the correct predicate is not obvious. + if (!S.getLangOpts().CPlusPlus && !S.CurContext->isFunctionOrMethod()) + return; - assert(FD && FD->isImmediateFunction() && - "could not find an immediate function in this expression"); - if (FD->isInvalidDecl()) + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety) { + // Allow capturing variables/fields in bounds attr expr context. + if (S.isAttrContext() && (isa(var) || isa(var))) return; - SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) - << FD << FD->isConsteval(); - if (auto Context = - SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { - SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) - << Context->Decl; - SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); - } - if (!FD->isConsteval()) - SemaRef.DiagnoseImmediateEscalatingReason(FD); - for (auto &Note : Notes) - SemaRef.Diag(Note.first, Note.second); - return; } - CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); -} - -static void RemoveNestedImmediateInvocation( - Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, - SmallVector::reverse_iterator It) { - struct ComplexRemove : TreeTransform { - using Base = TreeTransform; - llvm::SmallPtrSetImpl &DRSet; - SmallVector &IISet; - SmallVector::reverse_iterator - CurrentII; - ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, - SmallVector &II, - SmallVector::reverse_iterator Current) - : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} - void RemoveImmediateInvocation(ConstantExpr* E) { - auto It = std::find_if(CurrentII, IISet.rend(), - [E](Sema::ImmediateInvocationCandidate Elem) { - return Elem.getPointer() == E; - }); - // It is possible that some subexpression of the current immediate - // invocation was handled from another expression evaluation context. Do - // not handle the current immediate invocation if some of its - // subexpressions failed before. - if (It == IISet.rend()) { - if (SemaRef.FailedImmediateInvocations.contains(E)) - CurrentII->setInt(1); - } else { - It->setInt(1); // Mark as deleted - } - } - ExprResult TransformConstantExpr(ConstantExpr *E) { - if (!E->isImmediateInvocation()) - return Base::TransformConstantExpr(E); - RemoveImmediateInvocation(E); - return Base::TransformExpr(E->getSubExpr()); - } - /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so - /// we need to remove its DeclRefExpr from the DRSet. - ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { - DRSet.erase(cast(E->getCallee()->IgnoreImplicit())); - return Base::TransformCXXOperatorCallExpr(E); - } - /// Base::TransformUserDefinedLiteral doesn't preserve the - /// UserDefinedLiteral node. - ExprResult TransformUserDefinedLiteral(UserDefinedLiteral *E) { return E; } - /// Base::TransformInitializer skips ConstantExpr so we need to visit them - /// here. - ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) { - if (!Init) - return Init; - /// ConstantExpr are the first layer of implicit node to be removed so if - /// Init isn't a ConstantExpr, no ConstantExpr will be skipped. - if (auto *CE = dyn_cast(Init)) - if (CE->isImmediateInvocation()) - RemoveImmediateInvocation(CE); - return Base::TransformInitializer(Init, NotCopyInit); - } - ExprResult TransformDeclRefExpr(DeclRefExpr *E) { - DRSet.erase(E); - return E; - } - ExprResult TransformLambdaExpr(LambdaExpr *E) { - // Do not rebuild lambdas to avoid creating a new type. - // Lambdas have already been processed inside their eval context. - return E; - } - bool AlwaysRebuild() { return false; } - bool ReplacingOriginal() { return true; } - bool AllowSkippingCXXConstructExpr() { - bool Res = AllowSkippingFirstCXXConstructExpr; - AllowSkippingFirstCXXConstructExpr = true; - return Res; - } - bool AllowSkippingFirstCXXConstructExpr = true; - } Transformer(SemaRef, Rec.ReferenceToConsteval, - Rec.ImmediateInvocationCandidates, It); - - /// CXXConstructExpr with a single argument are getting skipped by - /// TreeTransform in some situtation because they could be implicit. This - /// can only occur for the top-level CXXConstructExpr because it is used - /// nowhere in the expression being transformed therefore will not be rebuilt. - /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from - /// skipping the first CXXConstructExpr. - if (isa(It->getPointer()->IgnoreImplicit())) - Transformer.AllowSkippingFirstCXXConstructExpr = false; + /* TO_UPSTREAM(BoundsSafety) OFF*/ - ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr()); - // The result may not be usable in case of previous compilation errors. - // In this case evaluation of the expression may result in crash so just - // don't do anything further with the result. - if (Res.isUsable()) { - Res = SemaRef.MaybeCreateExprWithCleanups(Res); - It->getPointer()->setSubExpr(Res.get()); + unsigned ValueKind = isa(var) ? 1 : 0; + unsigned ContextKind = 3; // unknown + if (isa(VarDC) && + cast(VarDC->getParent())->isLambda()) { + ContextKind = 2; + } else if (isa(VarDC)) { + ContextKind = 0; + } else if (isa(VarDC)) { + ContextKind = 1; } + + S.Diag(loc, diag::err_reference_to_local_in_enclosing_context) + << var << ValueKind << ContextKind << VarDC; + S.Diag(var->getLocation(), diag::note_entity_declared_at) + << var; + + // FIXME: Add additional diagnostic info about class etc. which prevents + // capture. } -static void -HandleImmediateInvocations(Sema &SemaRef, - Sema::ExpressionEvaluationContextRecord &Rec) { - if ((Rec.ImmediateInvocationCandidates.size() == 0 && - Rec.ReferenceToConsteval.size() == 0) || - Rec.isImmediateFunctionContext() || SemaRef.RebuildingImmediateInvocation) - return; +static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, + ValueDecl *Var, + bool &SubCapturesAreNested, + QualType &CaptureType, + QualType &DeclRefType) { + // Check whether we've already captured it. + if (CSI->CaptureMap.count(Var)) { + // If we found a capture, any subcaptures are nested. + SubCapturesAreNested = true; - /// When we have more than 1 ImmediateInvocationCandidates or previously - /// failed immediate invocations, we need to check for nested - /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. - /// Otherwise we only need to remove ReferenceToConsteval in the immediate - /// invocation. - if (Rec.ImmediateInvocationCandidates.size() > 1 || - !SemaRef.FailedImmediateInvocations.empty()) { + // Retrieve the capture type for this variable. + CaptureType = CSI->getCapture(Var).getCaptureType(); - /// Prevent sema calls during the tree transform from adding pointers that - /// are already in the sets. - llvm::SaveAndRestore DisableIITracking( - SemaRef.RebuildingImmediateInvocation, true); + // Compute the type of an expression that refers to this variable. + DeclRefType = CaptureType.getNonReferenceType(); - /// Prevent diagnostic during tree transfrom as they are duplicates - Sema::TentativeAnalysisScope DisableDiag(SemaRef); + // Similarly to mutable captures in lambda, all the OpenMP captures by copy + // are mutable in the sense that user can change their value - they are + // private instances of the captured declarations. + const Capture &Cap = CSI->getCapture(Var); + if (Cap.isCopyCapture() && + !(isa(CSI) && + !cast(CSI)->lambdaCaptureShouldBeConst()) && + !(isa(CSI) && + cast(CSI)->CapRegionKind == CR_OpenMP)) + DeclRefType.addConst(); + return true; + } + return false; +} - for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); - It != Rec.ImmediateInvocationCandidates.rend(); It++) - if (!It->getInt()) - RemoveNestedImmediateInvocation(SemaRef, Rec, It); - } else if (Rec.ImmediateInvocationCandidates.size() == 1 && - Rec.ReferenceToConsteval.size()) { - struct SimpleRemove : RecursiveASTVisitor { - llvm::SmallPtrSetImpl &DRSet; - SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} - bool VisitDeclRefExpr(DeclRefExpr *E) { - DRSet.erase(E); - return DRSet.size(); - } - } Visitor(Rec.ReferenceToConsteval); - Visitor.TraverseStmt( - Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); +// Only block literals, captured statements, and lambda expressions can +// capture; other scopes don't work. +static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, + ValueDecl *Var, + SourceLocation Loc, + const bool Diagnose, + Sema &S) { + if (isa(DC) || isa(DC) || isLambdaCallOperator(DC)) + return getLambdaAwareParentOfDeclContext(DC); + + VarDecl *Underlying = Var->getPotentiallyDecomposedVarDecl(); + if (Underlying) { + if (Underlying->hasLocalStorage() && Diagnose) + diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); } - for (auto CE : Rec.ImmediateInvocationCandidates) - if (!CE.getInt()) - EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); - for (auto *DR : Rec.ReferenceToConsteval) { - // If the expression is immediate escalating, it is not an error; - // The outer context itself becomes immediate and further errors, - // if any, will be handled by DiagnoseImmediateEscalatingReason. - if (DR->isImmediateEscalating()) - continue; - auto *FD = cast(DR->getDecl()); - const NamedDecl *ND = FD; - if (const auto *MD = dyn_cast(ND); - MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD))) - ND = MD->getParent(); + return nullptr; +} - // C++23 [expr.const]/p16 - // An expression or conversion is immediate-escalating if it is not - // initially in an immediate function context and it is [...] a - // potentially-evaluated id-expression that denotes an immediate function - // that is not a subexpression of an immediate invocation. - bool ImmediateEscalating = false; - bool IsPotentiallyEvaluated = - Rec.Context == - Sema::ExpressionEvaluationContext::PotentiallyEvaluated || - Rec.Context == - Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; - if (SemaRef.inTemplateInstantiation() && IsPotentiallyEvaluated) - ImmediateEscalating = Rec.InImmediateEscalatingFunctionContext; +// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture +// certain types of variables (unnamed, variably modified types etc.) +// so check for eligibility. +static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var, + SourceLocation Loc, const bool Diagnose, + Sema &S) { - if (!Rec.InImmediateEscalatingFunctionContext || - (SemaRef.inTemplateInstantiation() && !ImmediateEscalating)) { - SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) - << ND << isa(ND) << FD->isConsteval(); - SemaRef.Diag(ND->getLocation(), diag::note_declared_at); - if (auto Context = - SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { - SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) - << Context->Decl; - SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + assert((isa(Var)) && + "Only variables and structured bindings can be captured"); + + bool IsBlock = isa(CSI); + bool IsLambda = isa(CSI); + + // Lambdas are not allowed to capture unnamed variables + // (e.g. anonymous unions). + // FIXME: The C++11 rule don't actually state this explicitly, but I'm + // assuming that's the intent. + if (IsLambda && !Var->getDeclName()) { + if (Diagnose) { + S.Diag(Loc, diag::err_lambda_capture_anonymous_var); + S.Diag(Var->getLocation(), diag::note_declared_at); + } + return false; + } + + // Prohibit variably-modified types in blocks; they're difficult to deal with. + if (Var->getType()->isVariablyModifiedType() && IsBlock) { + if (Diagnose) { + S.Diag(Loc, diag::err_ref_vm_type); + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + } + return false; + } + // Prohibit structs with flexible array members too. + // We cannot capture what is in the tail end of the struct. + if (const RecordType *VTTy = Var->getType()->getAs()) { + if (VTTy->getDecl()->hasFlexibleArrayMember()) { + if (Diagnose) { + if (IsBlock) + S.Diag(Loc, diag::err_ref_flexarray_type); + else + S.Diag(Loc, diag::err_lambda_capture_flexarray_type) << Var; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; } - if (FD->isImmediateEscalating() && !FD->isConsteval()) - SemaRef.DiagnoseImmediateEscalatingReason(FD); + return false; + } + } + const bool HasBlocksAttr = Var->hasAttr(); + // Lambdas and captured statements are not allowed to capture __block + // variables; they don't support the expected semantics. + if (HasBlocksAttr && (IsLambda || isa(CSI))) { + if (Diagnose) { + S.Diag(Loc, diag::err_capture_block_variable) << Var << !IsLambda; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + } + return false; + } + // OpenCL v2.0 s6.12.5: Blocks cannot reference/capture other blocks + if (S.getLangOpts().OpenCL && IsBlock && + Var->getType()->isBlockPointerType()) { + if (Diagnose) + S.Diag(Loc, diag::err_opencl_block_ref_block); + return false; + } - } else { - SemaRef.MarkExpressionAsImmediateEscalating(DR); + if (isa(Var)) { + if (!IsLambda || !S.getLangOpts().CPlusPlus) { + if (Diagnose) + diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); + return false; + } else if (Diagnose && S.getLangOpts().CPlusPlus) { + S.Diag(Loc, S.LangOpts.CPlusPlus20 + ? diag::warn_cxx17_compat_capture_binding + : diag::ext_capture_binding) + << Var; + S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; } } -} -void Sema::PopExpressionEvaluationContext() { - ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); - unsigned NumTypos = Rec.NumTypos; + return true; +} - if (!Rec.Lambdas.empty()) { - using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; - if (!getLangOpts().CPlusPlus20 && - (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || - Rec.isUnevaluated() || - (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17))) { - unsigned D; - if (Rec.isUnevaluated()) { - // C++11 [expr.prim.lambda]p2: - // A lambda-expression shall not appear in an unevaluated operand - // (Clause 5). - D = diag::err_lambda_unevaluated_operand; - } else if (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17) { - // C++1y [expr.const]p2: - // A conditional-expression e is a core constant expression unless the - // evaluation of e, following the rules of the abstract machine, would - // evaluate [...] a lambda-expression. - D = diag::err_lambda_in_constant_expression; - } else if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument) { - // C++17 [expr.prim.lamda]p2: - // A lambda-expression shall not appear [...] in a template-argument. - D = diag::err_lambda_in_invalid_context; - } else - llvm_unreachable("Couldn't infer lambda error message."); +// Returns true if the capture by block was successful. +static bool captureInBlock(BlockScopeInfo *BSI, ValueDecl *Var, + SourceLocation Loc, const bool BuildAndDiagnose, + QualType &CaptureType, QualType &DeclRefType, + const bool Nested, Sema &S, bool Invalid) { + bool ByRef = false; - for (const auto *L : Rec.Lambdas) - Diag(L->getBeginLoc(), D); + // Blocks are not allowed to capture arrays, excepting OpenCL. + // OpenCL v2.0 s1.12.5 (revision 40): arrays are captured by reference + // (decayed to pointers). + if (!Invalid && !S.getLangOpts().OpenCL && CaptureType->isArrayType()) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_ref_array_type); + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Invalid = true; + } else { + return false; } } - // Append the collected materialized temporaries into previous context before - // exit if the previous also is a lifetime extending context. - auto &PrevRecord = parentEvaluationContext(); - if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && - PrevRecord.InLifetimeExtendingContext && - !Rec.ForRangeLifetimeExtendTemps.empty()) { - PrevRecord.ForRangeLifetimeExtendTemps.append( - Rec.ForRangeLifetimeExtendTemps); + // Forbid the block-capture of autoreleasing variables. + if (!Invalid && + CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_arc_autoreleasing_capture) + << /*block*/ 0; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Invalid = true; + } else { + return false; + } } - WarnOnPendingNoDerefs(Rec); - HandleImmediateInvocations(*this, Rec); + // Warn about implicitly autoreleasing indirect parameters captured by blocks. + if (const auto *PT = CaptureType->getAs()) { + QualType PointeeTy = PT->getPointeeType(); - // Warn on any volatile-qualified simple-assignments that are not discarded- - // value expressions nor unevaluated operands (those cases get removed from - // this list by CheckUnusedVolatileAssignment). - for (auto *BO : Rec.VolatileAssignmentLHSs) - Diag(BO->getBeginLoc(), diag::warn_deprecated_simple_assign_volatile) - << BO->getType(); + if (!Invalid && PointeeTy->getAs() && + PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing && + !S.Context.hasDirectOwnershipQualifier(PointeeTy)) { + if (BuildAndDiagnose) { + SourceLocation VarLoc = Var->getLocation(); + S.Diag(Loc, diag::warn_block_capture_autoreleasing); + S.Diag(VarLoc, diag::note_declare_parameter_strong); + } + } + } - // When are coming out of an unevaluated context, clear out any - // temporaries that we may have created as part of the evaluation of - // the expression in that context: they aren't relevant because they - // will never be constructed. - if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { - ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, - ExprCleanupObjects.end()); - Cleanup = Rec.ParentCleanup; - CleanupVarDeclMarking(); - std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); - // Otherwise, merge the contexts together. + const bool HasBlocksAttr = Var->hasAttr(); + if (HasBlocksAttr || CaptureType->isReferenceType() || + (S.getLangOpts().OpenMP && S.OpenMP().isOpenMPCapturedDecl(Var))) { + // Block capture by reference does not change the capture or + // declaration reference types. + ByRef = true; } else { - Cleanup.mergeFrom(Rec.ParentCleanup); - MaybeODRUseExprs.insert(Rec.SavedMaybeODRUseExprs.begin(), - Rec.SavedMaybeODRUseExprs.end()); + // Block capture by copy introduces 'const'. + CaptureType = CaptureType.getNonReferenceType().withConst(); + DeclRefType = CaptureType; } - // Pop the current expression evaluation context off the stack. - ExprEvalContexts.pop_back(); - - // The global expression evaluation context record is never popped. - ExprEvalContexts.back().NumTypos += NumTypos; -} - -void Sema::DiscardCleanupsInEvaluationContext() { - ExprCleanupObjects.erase( - ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, - ExprCleanupObjects.end()); - Cleanup.reset(); - MaybeODRUseExprs.clear(); -} + // Actually capture the variable. + if (BuildAndDiagnose) + BSI->addCapture(Var, HasBlocksAttr, ByRef, Nested, Loc, SourceLocation(), + CaptureType, Invalid); -ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) { - ExprResult Result = CheckPlaceholderExpr(E); - if (Result.isInvalid()) - return ExprError(); - E = Result.get(); - if (!E->getType()->isVariablyModifiedType()) - return E; - return TransformToPotentiallyEvaluated(E); + return !Invalid; } -/// Are we in a context that is potentially constant evaluated per C++20 -/// [expr.const]p12? -static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) { - /// C++2a [expr.const]p12: - // An expression or conversion is potentially constant evaluated if it is - switch (SemaRef.ExprEvalContexts.back().Context) { - case Sema::ExpressionEvaluationContext::ConstantEvaluated: - case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - - // -- a manifestly constant-evaluated expression, - case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: - case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - case Sema::ExpressionEvaluationContext::DiscardedStatement: - // -- a potentially-evaluated expression, - case Sema::ExpressionEvaluationContext::UnevaluatedList: - // -- an immediate subexpression of a braced-init-list, - - // -- [FIXME] an expression of the form & cast-expression that occurs - // within a templated entity - // -- a subexpression of one of the above that is not a subexpression of - // a nested unevaluated operand. +/// Capture the given variable in the captured region. +static bool captureInCapturedRegion( + CapturedRegionScopeInfo *RSI, ValueDecl *Var, SourceLocation Loc, + const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, + const bool RefersToCapturedVariable, Sema::TryCaptureKind Kind, + bool IsTopScope, Sema &S, bool Invalid) { + // By default, capture variables by reference. + bool ByRef = true; + if (IsTopScope && Kind != Sema::TryCapture_Implicit) { + ByRef = (Kind == Sema::TryCapture_ExplicitByRef); + } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) { + // Using an LValue reference type is consistent with Lambdas (see below). + if (S.OpenMP().isOpenMPCapturedDecl(Var)) { + bool HasConst = DeclRefType.isConstQualified(); + DeclRefType = DeclRefType.getUnqualifiedType(); + // Don't lose diagnostics about assignments to const. + if (HasConst) + DeclRefType.addConst(); + } + // Do not capture firstprivates in tasks. + if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel) != OMPC_unknown) return true; - - case Sema::ExpressionEvaluationContext::Unevaluated: - case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: - // Expressions in this context are never evaluated. - return false; + ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel); } - llvm_unreachable("Invalid context"); -} -/// Return true if this function has a calling convention that requires mangling -/// in the size of the parameter pack. -static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) { - // These manglings don't do anything on non-Windows or non-x86 platforms, so - // we don't need parameter type sizes. - const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); - if (!TT.isOSWindows() || !TT.isX86()) - return false; + if (ByRef) + CaptureType = S.Context.getLValueReferenceType(DeclRefType); + else + CaptureType = DeclRefType; - // If this is C++ and this isn't an extern "C" function, parameters do not - // need to be complete. In this case, C++ mangling will apply, which doesn't - // use the size of the parameters. - if (S.getLangOpts().CPlusPlus && !FD->isExternC()) - return false; + // Actually capture the variable. + if (BuildAndDiagnose) + RSI->addCapture(Var, /*isBlock*/ false, ByRef, RefersToCapturedVariable, + Loc, SourceLocation(), CaptureType, Invalid); - // Stdcall, fastcall, and vectorcall need this special treatment. - CallingConv CC = FD->getType()->castAs()->getCallConv(); - switch (CC) { - case CC_X86StdCall: - case CC_X86FastCall: - case CC_X86VectorCall: - return true; - default: - break; - } - return false; + return !Invalid; } -/// Require that all of the parameter types of function be complete. Normally, -/// parameter types are only required to be complete when a function is called -/// or defined, but to mangle functions with certain calling conventions, the -/// mangler needs to know the size of the parameter list. In this situation, -/// MSVC doesn't emit an error or instantiate templates. Instead, MSVC mangles -/// the function as _foo@0, i.e. zero bytes of parameters, which will usually -/// result in a linker error. Clang doesn't implement this behavior, and instead -/// attempts to error at compile time. -static void CheckCompleteParameterTypesForMangler(Sema &S, FunctionDecl *FD, - SourceLocation Loc) { - class ParamIncompleteTypeDiagnoser : public Sema::TypeDiagnoser { - FunctionDecl *FD; - ParmVarDecl *Param; - - public: - ParamIncompleteTypeDiagnoser(FunctionDecl *FD, ParmVarDecl *Param) - : FD(FD), Param(Param) {} - - void diagnose(Sema &S, SourceLocation Loc, QualType T) override { - CallingConv CC = FD->getType()->castAs()->getCallConv(); - StringRef CCName; - switch (CC) { - case CC_X86StdCall: - CCName = "stdcall"; - break; - case CC_X86FastCall: - CCName = "fastcall"; - break; - case CC_X86VectorCall: - CCName = "vectorcall"; - break; - default: - llvm_unreachable("CC does not need mangling"); - } - - S.Diag(Loc, diag::err_cconv_incomplete_param_type) - << Param->getDeclName() << FD->getDeclName() << CCName; - } - }; - - for (ParmVarDecl *Param : FD->parameters()) { - ParamIncompleteTypeDiagnoser Diagnoser(FD, Param); - S.RequireCompleteType(Loc, Param->getType(), Diagnoser); +/// Capture the given variable in the lambda. +static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, + SourceLocation Loc, const bool BuildAndDiagnose, + QualType &CaptureType, QualType &DeclRefType, + const bool RefersToCapturedVariable, + const Sema::TryCaptureKind Kind, + SourceLocation EllipsisLoc, const bool IsTopScope, + Sema &S, bool Invalid) { + // Determine whether we are capturing by reference or by value. + bool ByRef = false; + if (IsTopScope && Kind != Sema::TryCapture_Implicit) { + ByRef = (Kind == Sema::TryCapture_ExplicitByRef); + } else { + ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref); } -} - -namespace { -enum class OdrUseContext { - /// Declarations in this context are not odr-used. - None, - /// Declarations in this context are formally odr-used, but this is a - /// dependent context. - Dependent, - /// Declarations in this context are odr-used but not actually used (yet). - FormallyOdrUsed, - /// Declarations in this context are used. - Used -}; -} -/// Are we within a context in which references to resolved functions or to -/// variables result in odr-use? -static OdrUseContext isOdrUseContext(Sema &SemaRef) { - OdrUseContext Result; + if (BuildAndDiagnose && S.Context.getTargetInfo().getTriple().isWasm() && + CaptureType.getNonReferenceType().isWebAssemblyReferenceType()) { + S.Diag(Loc, diag::err_wasm_ca_reference) << 0; + Invalid = true; + } - switch (SemaRef.ExprEvalContexts.back().Context) { - case Sema::ExpressionEvaluationContext::Unevaluated: - case Sema::ExpressionEvaluationContext::UnevaluatedList: - case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: - return OdrUseContext::None; + // Compute the type of the field that will capture this variable. + if (ByRef) { + // C++11 [expr.prim.lambda]p15: + // An entity is captured by reference if it is implicitly or + // explicitly captured but not captured by copy. It is + // unspecified whether additional unnamed non-static data + // members are declared in the closure type for entities + // captured by reference. + // + // FIXME: It is not clear whether we want to build an lvalue reference + // to the DeclRefType or to CaptureType.getNonReferenceType(). GCC appears + // to do the former, while EDG does the latter. Core issue 1249 will + // clarify, but for now we follow GCC because it's a more permissive and + // easily defensible position. + CaptureType = S.Context.getLValueReferenceType(DeclRefType); + } else { + // C++11 [expr.prim.lambda]p14: + // For each entity captured by copy, an unnamed non-static + // data member is declared in the closure type. The + // declaration order of these members is unspecified. The type + // of such a data member is the type of the corresponding + // captured entity if the entity is not a reference to an + // object, or the referenced type otherwise. [Note: If the + // captured entity is a reference to a function, the + // corresponding data member is also a reference to a + // function. - end note ] + if (const ReferenceType *RefType = CaptureType->getAs()){ + if (!RefType->getPointeeType()->isFunctionType()) + CaptureType = RefType->getPointeeType(); + } - case Sema::ExpressionEvaluationContext::ConstantEvaluated: - case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: - Result = OdrUseContext::Used; - break; + // Forbid the lambda copy-capture of autoreleasing variables. + if (!Invalid && + CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_arc_autoreleasing_capture) << /*lambda*/ 1; + S.Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + Invalid = true; + } else { + return false; + } + } - case Sema::ExpressionEvaluationContext::DiscardedStatement: - Result = OdrUseContext::FormallyOdrUsed; - break; + // Make sure that by-copy captures are of a complete and non-abstract type. + if (!Invalid && BuildAndDiagnose) { + if (!CaptureType->isDependentType() && + S.RequireCompleteSizedType( + Loc, CaptureType, + diag::err_capture_of_incomplete_or_sizeless_type, + Var->getDeclName())) + Invalid = true; + else if (S.RequireNonAbstractType(Loc, CaptureType, + diag::err_capture_of_abstract_type)) + Invalid = true; + } + } - case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - // A default argument formally results in odr-use, but doesn't actually - // result in a use in any real sense until it itself is used. - Result = OdrUseContext::FormallyOdrUsed; - break; + // Compute the type of a reference to this captured variable. + if (ByRef) + DeclRefType = CaptureType.getNonReferenceType(); + else { + // C++ [expr.prim.lambda]p5: + // The closure type for a lambda-expression has a public inline + // function call operator [...]. This function call operator is + // declared const (9.3.1) if and only if the lambda-expression's + // parameter-declaration-clause is not followed by mutable. + DeclRefType = CaptureType.getNonReferenceType(); + bool Const = LSI->lambdaCaptureShouldBeConst(); + if (Const && !CaptureType->isReferenceType()) + DeclRefType.addConst(); } - if (SemaRef.CurContext->isDependentContext()) - return OdrUseContext::Dependent; + // Add the capture. + if (BuildAndDiagnose) + LSI->addCapture(Var, /*isBlock=*/false, ByRef, RefersToCapturedVariable, + Loc, EllipsisLoc, CaptureType, Invalid); - return Result; + return !Invalid; } -static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { - if (!Func->isConstexpr()) - return false; - - if (Func->isImplicitlyInstantiable() || !Func->isUserProvided()) +static bool canCaptureVariableByCopy(ValueDecl *Var, + const ASTContext &Context) { + // Offer a Copy fix even if the type is dependent. + if (Var->getType()->isDependentType()) return true; - auto *CCD = dyn_cast(Func); - return CCD && CCD->getInheritedConstructor(); -} - -void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, - bool MightBeOdrUse) { - assert(Func && "No function?"); - - Func->setReferenced(); + QualType T = Var->getType().getNonReferenceType(); + if (T.isTriviallyCopyableType(Context)) + return true; + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { - // Recursive functions aren't really used until they're used from some other - // context. - bool IsRecursiveCall = CurContext == Func; + if (!(RD = RD->getDefinition())) + return false; + if (RD->hasSimpleCopyConstructor()) + return true; + if (RD->hasUserDeclaredCopyConstructor()) + for (CXXConstructorDecl *Ctor : RD->ctors()) + if (Ctor->isCopyConstructor()) + return !Ctor->isDeleted(); + } + return false; +} - // C++11 [basic.def.odr]p3: - // A function whose name appears as a potentially-evaluated expression is - // odr-used if it is the unique lookup result or the selected member of a - // set of overloaded functions [...]. - // - // We (incorrectly) mark overload resolution as an unevaluated context, so we - // can just check that here. - OdrUseContext OdrUse = - MightBeOdrUse ? isOdrUseContext(*this) : OdrUseContext::None; - if (IsRecursiveCall && OdrUse == OdrUseContext::Used) - OdrUse = OdrUseContext::FormallyOdrUsed; +/// Create up to 4 fix-its for explicit reference and value capture of \p Var or +/// default capture. Fixes may be omitted if they aren't allowed by the +/// standard, for example we can't emit a default copy capture fix-it if we +/// already explicitly copy capture capture another variable. +static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI, + ValueDecl *Var) { + assert(LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None); + // Don't offer Capture by copy of default capture by copy fixes if Var is + // known not to be copy constructible. + bool ShouldOfferCopyFix = canCaptureVariableByCopy(Var, Sema.getASTContext()); - // Trivial default constructors and destructors are never actually used. - // FIXME: What about other special members? - if (Func->isTrivial() && !Func->hasAttr() && - OdrUse == OdrUseContext::Used) { - if (auto *Constructor = dyn_cast(Func)) - if (Constructor->isDefaultConstructor()) - OdrUse = OdrUseContext::FormallyOdrUsed; - if (isa(Func)) - OdrUse = OdrUseContext::FormallyOdrUsed; + SmallString<32> FixBuffer; + StringRef Separator = LSI->NumExplicitCaptures > 0 ? ", " : ""; + if (Var->getDeclName().isIdentifier() && !Var->getName().empty()) { + SourceLocation VarInsertLoc = LSI->IntroducerRange.getEnd(); + if (ShouldOfferCopyFix) { + // Offer fixes to insert an explicit capture for the variable. + // [] -> [VarName] + // [OtherCapture] -> [OtherCapture, VarName] + FixBuffer.assign({Separator, Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*value*/ 0 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + } + // As above but capture by reference. + FixBuffer.assign({Separator, "&", Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*reference*/ 1 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); } - // C++20 [expr.const]p12: - // A function [...] is needed for constant evaluation if it is [...] a - // constexpr function that is named by an expression that is potentially - // constant evaluated - bool NeededForConstantEvaluation = - isPotentiallyConstantEvaluatedContext(*this) && - isImplicitlyDefinableConstexprFunction(Func); + // Only try to offer default capture if there are no captures excluding this + // and init captures. + // [this]: OK. + // [X = Y]: OK. + // [&A, &B]: Don't offer. + // [A, B]: Don't offer. + if (llvm::any_of(LSI->Captures, [](Capture &C) { + return !C.isThisCapture() && !C.isInitCapture(); + })) + return; - // Determine whether we require a function definition to exist, per - // C++11 [temp.inst]p3: - // Unless a function template specialization has been explicitly - // instantiated or explicitly specialized, the function template - // specialization is implicitly instantiated when the specialization is - // referenced in a context that requires a function definition to exist. - // C++20 [temp.inst]p7: - // The existence of a definition of a [...] function is considered to - // affect the semantics of the program if the [...] function is needed for - // constant evaluation by an expression - // C++20 [basic.def.odr]p10: - // Every program shall contain exactly one definition of every non-inline - // function or variable that is odr-used in that program outside of a - // discarded statement - // C++20 [special]p1: - // The implementation will implicitly define [defaulted special members] - // if they are odr-used or needed for constant evaluation. - // - // Note that we skip the implicit instantiation of templates that are only - // used in unused default arguments or by recursive calls to themselves. - // This is formally non-conforming, but seems reasonable in practice. - bool NeedDefinition = - !IsRecursiveCall && - (OdrUse == OdrUseContext::Used || - (NeededForConstantEvaluation && !Func->isPureVirtual())); + // The default capture specifiers, '=' or '&', must appear first in the + // capture body. + SourceLocation DefaultInsertLoc = + LSI->IntroducerRange.getBegin().getLocWithOffset(1); - // C++14 [temp.expl.spec]p6: - // If a template [...] is explicitly specialized then that specialization - // shall be declared before the first use of that specialization that would - // cause an implicit instantiation to take place, in every translation unit - // in which such a use occurs - if (NeedDefinition && - (Func->getTemplateSpecializationKind() != TSK_Undeclared || - Func->getMemberSpecializationInfo())) - checkSpecializationReachability(Loc, Func); + if (ShouldOfferCopyFix) { + bool CanDefaultCopyCapture = true; + // [=, *this] OK since c++17 + // [=, this] OK since c++20 + if (LSI->isCXXThisCaptured() && !Sema.getLangOpts().CPlusPlus20) + CanDefaultCopyCapture = Sema.getLangOpts().CPlusPlus17 + ? LSI->getCXXThisCapture().isCopyCapture() + : false; + // We can't use default capture by copy if any captures already specified + // capture by copy. + if (CanDefaultCopyCapture && llvm::none_of(LSI->Captures, [](Capture &C) { + return !C.isThisCapture() && !C.isInitCapture() && C.isCopyCapture(); + })) { + FixBuffer.assign({"=", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*value*/ 0 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + } + } - if (getLangOpts().CUDA) - CUDA().CheckCall(Loc, Func); + // We can't use default capture by reference if any captures already specified + // capture by reference. + if (llvm::none_of(LSI->Captures, [](Capture &C) { + return !C.isInitCapture() && C.isReferenceCapture() && + !C.isThisCapture(); + })) { + FixBuffer.assign({"&", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*reference*/ 1 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + } +} - // If we need a definition, try to create one. - if (NeedDefinition && !Func->getBody()) { - runWithSufficientStackSpace(Loc, [&] { - if (CXXConstructorDecl *Constructor = - dyn_cast(Func)) { - Constructor = cast(Constructor->getFirstDecl()); - if (Constructor->isDefaulted() && !Constructor->isDeleted()) { - if (Constructor->isDefaultConstructor()) { - if (Constructor->isTrivial() && - !Constructor->hasAttr()) - return; - DefineImplicitDefaultConstructor(Loc, Constructor); - } else if (Constructor->isCopyConstructor()) { - DefineImplicitCopyConstructor(Loc, Constructor); - } else if (Constructor->isMoveConstructor()) { - DefineImplicitMoveConstructor(Loc, Constructor); - } - } else if (Constructor->getInheritedConstructor()) { - DefineInheritingConstructor(Loc, Constructor); - } - } else if (CXXDestructorDecl *Destructor = - dyn_cast(Func)) { - Destructor = cast(Destructor->getFirstDecl()); - if (Destructor->isDefaulted() && !Destructor->isDeleted()) { - if (Destructor->isTrivial() && !Destructor->hasAttr()) - return; - DefineImplicitDestructor(Loc, Destructor); - } - if (Destructor->isVirtual() && getLangOpts().AppleKext) - MarkVTableUsed(Loc, Destructor->getParent()); - } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { - if (MethodDecl->isOverloadedOperator() && - MethodDecl->getOverloadedOperator() == OO_Equal) { - MethodDecl = cast(MethodDecl->getFirstDecl()); - if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { - if (MethodDecl->isCopyAssignmentOperator()) - DefineImplicitCopyAssignment(Loc, MethodDecl); - else if (MethodDecl->isMoveAssignmentOperator()) - DefineImplicitMoveAssignment(Loc, MethodDecl); - } - } else if (isa(MethodDecl) && - MethodDecl->getParent()->isLambda()) { - CXXConversionDecl *Conversion = - cast(MethodDecl->getFirstDecl()); - if (Conversion->isLambdaToBlockPointerConversion()) - DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); - else - DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); - } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) - MarkVTableUsed(Loc, MethodDecl->getParent()); - } +bool Sema::tryCaptureVariable( + ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, + SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, + QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { + // An init-capture is notionally from the context surrounding its + // declaration, but its parent DC is the lambda class. + DeclContext *VarDC = Var->getDeclContext(); + DeclContext *DC = CurContext; - if (Func->isDefaulted() && !Func->isDeleted()) { - DefaultedComparisonKind DCK = getDefaultedComparisonKind(Func); - if (DCK != DefaultedComparisonKind::None) - DefineDefaultedComparison(Loc, Func, DCK); - } + // Skip past RequiresExprBodys because they don't constitute function scopes. + while (DC->isRequiresExprBody()) + DC = DC->getParent(); - // Implicit instantiation of function templates and member functions of - // class templates. - if (Func->isImplicitlyInstantiable()) { - TemplateSpecializationKind TSK = - Func->getTemplateSpecializationKindForInstantiation(); - SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); - bool FirstInstantiation = PointOfInstantiation.isInvalid(); - if (FirstInstantiation) { - PointOfInstantiation = Loc; - if (auto *MSI = Func->getMemberSpecializationInfo()) - MSI->setPointOfInstantiation(Loc); - // FIXME: Notify listener. - else - Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); - } else if (TSK != TSK_ImplicitInstantiation) { - // Use the point of use as the point of instantiation, instead of the - // point of explicit instantiation (which we track as the actual point - // of instantiation). This gives better backtraces in diagnostics. - PointOfInstantiation = Loc; - } + // tryCaptureVariable is called every time a DeclRef is formed, + // it can therefore have non-negigible impact on performances. + // For local variables and when there is no capturing scope, + // we can bailout early. + if (CapturingFunctionScopes == 0 && (!BuildAndDiagnose || VarDC == DC)) + return true; - if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || - Func->isConstexpr()) { - if (isa(Func->getDeclContext()) && - cast(Func->getDeclContext())->isLocalClass() && - CodeSynthesisContexts.size()) - PendingLocalImplicitInstantiations.push_back( - std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) - // Do not defer instantiations of constexpr functions, to avoid the - // expression evaluator needing to call back into Sema if it sees a - // call to such a function. - InstantiateFunctionDefinition(PointOfInstantiation, Func); - else { - Func->setInstantiationIsPending(true); - PendingInstantiations.push_back( - std::make_pair(Func, PointOfInstantiation)); - // Notify the consumer that a function was implicitly instantiated. - Consumer.HandleCXXImplicitFunctionInstantiation(Func); - } - } - } else { - // Walk redefinitions, as some of them may be instantiable. - for (auto *i : Func->redecls()) { - if (!i->isUsed(false) && i->isImplicitlyInstantiable()) - MarkFunctionReferenced(Loc, i, MightBeOdrUse); - } - } - }); + // Exception: Function parameters are not tied to the function's DeclContext + // until we enter the function definition. Capturing them anyway would result + // in an out-of-bounds error while traversing DC and its parents. + if (isa(Var) && !VarDC->isFunctionOrMethod()) + return true; + + const auto *VD = dyn_cast(Var); + if (VD) { + if (VD->isInitCapture()) + VarDC = VarDC->getParent(); + } else { + VD = Var->getPotentiallyDecomposedVarDecl(); } + assert(VD && "Cannot capture a null variable"); - // If a constructor was defined in the context of a default parameter - // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed - // context), its initializers may not be referenced yet. - if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { - EnterExpressionEvaluationContext EvalContext( - *this, - Constructor->isImmediateFunction() - ? ExpressionEvaluationContext::ImmediateFunctionContext - : ExpressionEvaluationContext::PotentiallyEvaluated, - Constructor); - for (CXXCtorInitializer *Init : Constructor->inits()) { - if (Init->isInClassMemberInitializer()) - runWithSufficientStackSpace(Init->getSourceLocation(), [&]() { - MarkDeclarationsReferencedInExpr(Init->getInit()); - }); + const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt + ? *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; + // We need to sync up the Declaration Context with the + // FunctionScopeIndexToStopAt + if (FunctionScopeIndexToStopAt) { + unsigned FSIndex = FunctionScopes.size() - 1; + while (FSIndex != MaxFunctionScopesIndex) { + DC = getLambdaAwareParentOfDeclContext(DC); + --FSIndex; } } - // C++14 [except.spec]p17: - // An exception-specification is considered to be needed when: - // - the function is odr-used or, if it appears in an unevaluated operand, - // would be odr-used if the expression were potentially-evaluated; - // - // Note, we do this even if MightBeOdrUse is false. That indicates that the - // function is a pure virtual function we're calling, and in that case the - // function was selected by overload resolution and we need to resolve its - // exception specification for a different reason. - const FunctionProtoType *FPT = Func->getType()->getAs(); - if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) - ResolveExceptionSpec(Loc, FPT); + // Capture global variables if it is required to use private copy of this + // variable. + bool IsGlobal = !VD->hasLocalStorage(); + if (IsGlobal && !(LangOpts.OpenMP && + OpenMP().isOpenMPCapturedDecl(Var, /*CheckScopeInfo=*/true, + MaxFunctionScopesIndex))) + return true; - // A callee could be called by a host function then by a device function. - // If we only try recording once, we will miss recording the use on device - // side. Therefore keep trying until it is recorded. - if (LangOpts.OffloadImplicitHostDeviceTemplates && LangOpts.CUDAIsDevice && - !getASTContext().CUDAImplicitHostDeviceFunUsedByDevice.count(Func)) - CUDA().RecordImplicitHostDeviceFuncUsedByDevice(Func); + if (isa(Var)) + Var = cast(Var->getCanonicalDecl()); + + // Walk up the stack to determine whether we can capture the variable, + // performing the "simple" checks that don't depend on type. We stop when + // we've either hit the declared scope of the variable or find an existing + // capture of that variable. We start from the innermost capturing-entity + // (the DC) and ensure that all intervening capturing-entities + // (blocks/lambdas etc.) between the innermost capturer and the variable`s + // declcontext can either capture the variable or have already captured + // the variable. + CaptureType = Var->getType(); + DeclRefType = CaptureType.getNonReferenceType(); + bool Nested = false; + bool Explicit = (Kind != TryCapture_Implicit); + unsigned FunctionScopesIndex = MaxFunctionScopesIndex; + do { + + LambdaScopeInfo *LSI = nullptr; + if (!FunctionScopes.empty()) + LSI = dyn_cast_or_null( + FunctionScopes[FunctionScopesIndex]); + + bool IsInScopeDeclarationContext = + !LSI || LSI->AfterParameterList || CurContext == LSI->CallOperator; + + if (LSI && !LSI->AfterParameterList) { + // This allows capturing parameters from a default value which does not + // seems correct + if (isa(Var) && !Var->getDeclContext()->isFunctionOrMethod()) + return true; + } + // If the variable is declared in the current context, there is no need to + // capture it. + if (IsInScopeDeclarationContext && + FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC) + return true; + + // Only block literals, captured statements, and lambda expressions can + // capture; other scopes don't work. + DeclContext *ParentDC = + !IsInScopeDeclarationContext + ? DC->getParent() + : getParentOfCapturingContextOrNull(DC, Var, ExprLoc, + BuildAndDiagnose, *this); + // We need to check for the parent *first* because, if we *have* + // private-captured a global variable, we need to recursively capture it in + // intermediate blocks, lambdas, etc. + if (!ParentDC) { + if (IsGlobal) { + FunctionScopesIndex = MaxFunctionScopesIndex - 1; + break; + } + return true; + } - // If this is the first "real" use, act on that. - if (OdrUse == OdrUseContext::Used && !Func->isUsed(/*CheckUsedAttr=*/false)) { - // Keep track of used but undefined functions. - if (!Func->isDefined()) { - if (mightHaveNonExternalLinkage(Func)) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); - else if (Func->getMostRecentDecl()->isInlined() && - !LangOpts.GNUInline && - !Func->getMostRecentDecl()->hasAttr()) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); - else if (isExternalWithNoLinkageType(Func)) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; + CapturingScopeInfo *CSI = cast(FSI); + + // Check whether we've already captured it. + if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, + DeclRefType)) { + CSI->getCapture(Var).markUsed(BuildAndDiagnose); + break; } - // Some x86 Windows calling conventions mangle the size of the parameter - // pack into the name. Computing the size of the parameters requires the - // parameter types to be complete. Check that now. - if (funcHasParameterSizeMangling(*this, Func)) - CheckCompleteParameterTypesForMangler(*this, Func, Loc); + // When evaluating some attributes (like enable_if) we might refer to a + // function parameter appertaining to the same declaration as that + // attribute. + if (const auto *Parm = dyn_cast(Var); + Parm && Parm->getDeclContext() == DC) + return true; - // In the MS C++ ABI, the compiler emits destructor variants where they are - // used. If the destructor is used here but defined elsewhere, mark the - // virtual base destructors referenced. If those virtual base destructors - // are inline, this will ensure they are defined when emitting the complete - // destructor variant. This checking may be redundant if the destructor is - // provided later in this TU. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { - if (auto *Dtor = dyn_cast(Func)) { - CXXRecordDecl *Parent = Dtor->getParent(); - if (Parent->getNumVBases() > 0 && !Dtor->getBody()) - CheckCompleteDestructorVariant(Loc, Dtor); + // If we are instantiating a generic lambda call operator body, + // we do not want to capture new variables. What was captured + // during either a lambdas transformation or initial parsing + // should be used. + if (isGenericLambdaCallOperatorSpecialization(DC)) { + if (BuildAndDiagnose) { + LambdaScopeInfo *LSI = cast(CSI); + if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { + Diag(ExprLoc, diag::err_lambda_impcap) << Var; + Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, LSI, Var); + } else + diagnoseUncapturableValueReferenceOrBinding(*this, ExprLoc, Var); } + return true; } - Func->markUsed(Context); - } -} + // Try to capture variable-length arrays types. + if (Var->getType()->isVariablyModifiedType()) { + // We're going to walk down into the type and look for VLA + // expressions. + QualType QTy = Var->getType(); + if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) + QTy = PVD->getOriginalType(); + captureVariablyModifiedType(Context, QTy, CSI); + } -/// Directly mark a variable odr-used. Given a choice, prefer to use -/// MarkVariableReferenced since it does additional checks and then -/// calls MarkVarDeclODRUsed. -/// If the variable must be captured: -/// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext -/// - else capture it in the DeclContext that maps to the -/// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack. -static void -MarkVarDeclODRUsed(ValueDecl *V, SourceLocation Loc, Sema &SemaRef, - const unsigned *const FunctionScopeIndexToStopAt = nullptr) { - // Keep track of used but undefined variables. - // FIXME: We shouldn't suppress this warning for static data members. - VarDecl *Var = V->getPotentiallyDecomposedVarDecl(); - assert(Var && "expected a capturable variable"); + if (getLangOpts().OpenMP) { + if (auto *RSI = dyn_cast(CSI)) { + // OpenMP private variables should not be captured in outer scope, so + // just break here. Similarly, global variables that are captured in a + // target region should not be captured outside the scope of the region. + if (RSI->CapRegionKind == CR_OpenMP) { + // FIXME: We should support capturing structured bindings in OpenMP. + if (isa(Var)) { + if (BuildAndDiagnose) { + Diag(ExprLoc, diag::err_capture_binding_openmp) << Var; + Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; + } + return true; + } + OpenMPClauseKind IsOpenMPPrivateDecl = OpenMP().isOpenMPPrivateDecl( + Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); + // If the variable is private (i.e. not captured) and has variably + // modified type, we still need to capture the type for correct + // codegen in all regions, associated with the construct. Currently, + // it is captured in the innermost captured region only. + if (IsOpenMPPrivateDecl != OMPC_unknown && + Var->getType()->isVariablyModifiedType()) { + QualType QTy = Var->getType(); + if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) + QTy = PVD->getOriginalType(); + for (int I = 1, + E = OpenMP().getNumberOfConstructScopes(RSI->OpenMPLevel); + I < E; ++I) { + auto *OuterRSI = cast( + FunctionScopes[FunctionScopesIndex - I]); + assert(RSI->OpenMPLevel == OuterRSI->OpenMPLevel && + "Wrong number of captured regions associated with the " + "OpenMP construct."); + captureVariablyModifiedType(Context, QTy, OuterRSI); + } + } + bool IsTargetCap = + IsOpenMPPrivateDecl != OMPC_private && + OpenMP().isOpenMPTargetCapturedDecl(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel); + // Do not capture global if it is not privatized in outer regions. + bool IsGlobalCap = + IsGlobal && OpenMP().isOpenMPGlobalCapturedDecl( + Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); - if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && - (!Var->isExternallyVisible() || Var->isInline() || - SemaRef.isExternalWithNoLinkageType(Var)) && - !(Var->isStaticDataMember() && Var->hasInit())) { - SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; - if (old.isInvalid()) - old = Loc; - } - QualType CaptureType, DeclRefType; - if (SemaRef.LangOpts.OpenMP) - SemaRef.OpenMP().tryCaptureOpenMPLambdas(V); - SemaRef.tryCaptureVariable(V, Loc, Sema::TryCapture_Implicit, - /*EllipsisLoc*/ SourceLocation(), - /*BuildAndDiagnose*/ true, CaptureType, - DeclRefType, FunctionScopeIndexToStopAt); + // When we detect target captures we are looking from inside the + // target region, therefore we need to propagate the capture from the + // enclosing region. Therefore, the capture is not initially nested. + if (IsTargetCap) + OpenMP().adjustOpenMPTargetScopeIndex(FunctionScopesIndex, + RSI->OpenMPLevel); - if (SemaRef.LangOpts.CUDA && Var->hasGlobalStorage()) { - auto *FD = dyn_cast_or_null(SemaRef.CurContext); - auto VarTarget = SemaRef.CUDA().IdentifyTarget(Var); - auto UserTarget = SemaRef.CUDA().IdentifyTarget(FD); - if (VarTarget == SemaCUDA::CVT_Host && - (UserTarget == CUDAFunctionTarget::Device || - UserTarget == CUDAFunctionTarget::HostDevice || - UserTarget == CUDAFunctionTarget::Global)) { - // Diagnose ODR-use of host global variables in device functions. - // Reference of device global variables in host functions is allowed - // through shadow variables therefore it is not diagnosed. - if (SemaRef.LangOpts.CUDAIsDevice && !SemaRef.LangOpts.HIPStdPar) { - SemaRef.targetDiag(Loc, diag::err_ref_bad_target) - << /*host*/ 2 << /*variable*/ 1 << Var - << llvm::to_underlying(UserTarget); - SemaRef.targetDiag(Var->getLocation(), - Var->getType().isConstQualified() - ? diag::note_cuda_const_var_unpromoted - : diag::note_cuda_host_var); + if (IsTargetCap || IsOpenMPPrivateDecl == OMPC_private || + (IsGlobal && !IsGlobalCap)) { + Nested = !IsTargetCap; + bool HasConst = DeclRefType.isConstQualified(); + DeclRefType = DeclRefType.getUnqualifiedType(); + // Don't lose diagnostics about assignments to const. + if (HasConst) + DeclRefType.addConst(); + CaptureType = Context.getLValueReferenceType(DeclRefType); + break; + } + } } - } else if (VarTarget == SemaCUDA::CVT_Device && - !Var->hasAttr() && - (UserTarget == CUDAFunctionTarget::Host || - UserTarget == CUDAFunctionTarget::HostDevice)) { - // Record a CUDA/HIP device side variable if it is ODR-used - // by host code. This is done conservatively, when the variable is - // referenced in any of the following contexts: - // - a non-function context - // - a host function - // - a host device function - // This makes the ODR-use of the device side variable by host code to - // be visible in the device compilation for the compiler to be able to - // emit template variables instantiated by host code only and to - // externalize the static device side variable ODR-used by host code. - if (!Var->hasExternalStorage()) - SemaRef.getASTContext().CUDADeviceVarODRUsedByHost.insert(Var); - else if (SemaRef.LangOpts.GPURelocatableDeviceCode && - (!FD || (!FD->getDescribedFunctionTemplate() && - SemaRef.getASTContext().GetGVALinkageForFunction(FD) == - GVA_StrongExternal))) - SemaRef.getASTContext().CUDAExternalDeviceDeclODRUsedByHost.insert(Var); } - } - - V->markUsed(SemaRef.Context); -} + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { + // No capture-default, and this is not an explicit capture + // so cannot capture this variable. + if (BuildAndDiagnose) { + Diag(ExprLoc, diag::err_lambda_impcap) << Var; + Diag(Var->getLocation(), diag::note_previous_decl) << Var; + auto *LSI = cast(CSI); + if (LSI->Lambda) { + Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, LSI, Var); + } + // FIXME: If we error out because an outer lambda can not implicitly + // capture a variable that an inner lambda explicitly captures, we + // should have the inner lambda do the explicit capture - because + // it makes for cleaner diagnostics later. This would purely be done + // so that the diagnostic does not misleadingly claim that a variable + // can not be captured by a lambda implicitly even though it is captured + // explicitly. Suggestion: + // - create const bool VariableCaptureWasInitiallyExplicit = Explicit + // at the function head + // - cache the StartingDeclContext - this must be a lambda + // - captureInLambda in the innermost lambda the variable. + } + return true; + } + Explicit = false; + FunctionScopesIndex--; + if (IsInScopeDeclarationContext) + DC = ParentDC; + } while (!VarDC->Equals(DC)); -void Sema::MarkCaptureUsedInEnclosingContext(ValueDecl *Capture, - SourceLocation Loc, - unsigned CapturingScopeIndex) { - MarkVarDeclODRUsed(Capture, Loc, *this, &CapturingScopeIndex); -} + // Walk back down the scope stack, (e.g. from outer lambda to inner lambda) + // computing the type of the capture at each step, checking type-specific + // requirements, and adding captures if requested. + // If the variable had already been captured previously, we start capturing + // at the lambda nested within that one. + bool Invalid = false; + for (unsigned I = ++FunctionScopesIndex, N = MaxFunctionScopesIndex + 1; I != N; + ++I) { + CapturingScopeInfo *CSI = cast(FunctionScopes[I]); -void diagnoseUncapturableValueReferenceOrBinding(Sema &S, SourceLocation loc, - ValueDecl *var) { - DeclContext *VarDC = var->getDeclContext(); + // Certain capturing entities (lambdas, blocks etc.) are not allowed to capture + // certain types of variables (unnamed, variably modified types etc.) + // so check for eligibility. + if (!Invalid) + Invalid = + !isVariableCapturable(CSI, Var, ExprLoc, BuildAndDiagnose, *this); - // If the parameter still belongs to the translation unit, then - // we're actually just using one parameter in the declaration of - // the next. - if (isa(var) && - isa(VarDC)) - return; + // After encountering an error, if we're actually supposed to capture, keep + // capturing in nested contexts to suppress any follow-on diagnostics. + if (Invalid && !BuildAndDiagnose) + return true; - // For C code, don't diagnose about capture if we're not actually in code - // right now; it's impossible to write a non-constant expression outside of - // function context, so we'll get other (more useful) diagnostics later. - // - // For C++, things get a bit more nasty... it would be nice to suppress this - // diagnostic for certain cases like using a local variable in an array bound - // for a member of a local class, but the correct predicate is not obvious. - if (!S.getLangOpts().CPlusPlus && !S.CurContext->isFunctionOrMethod()) - return; + if (BlockScopeInfo *BSI = dyn_cast(CSI)) { + Invalid = !captureInBlock(BSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, + DeclRefType, Nested, *this, Invalid); + Nested = true; + } else if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { + Invalid = !captureInCapturedRegion( + RSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, + Kind, /*IsTopScope*/ I == N - 1, *this, Invalid); + Nested = true; + } else { + LambdaScopeInfo *LSI = cast(CSI); + Invalid = + !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, + DeclRefType, Nested, Kind, EllipsisLoc, + /*IsTopScope*/ I == N - 1, *this, Invalid); + Nested = true; + } - unsigned ValueKind = isa(var) ? 1 : 0; - unsigned ContextKind = 3; // unknown - if (isa(VarDC) && - cast(VarDC->getParent())->isLambda()) { - ContextKind = 2; - } else if (isa(VarDC)) { - ContextKind = 0; - } else if (isa(VarDC)) { - ContextKind = 1; + if (Invalid && !BuildAndDiagnose) + return true; } + return Invalid; +} - S.Diag(loc, diag::err_reference_to_local_in_enclosing_context) - << var << ValueKind << ContextKind << VarDC; - S.Diag(var->getLocation(), diag::note_entity_declared_at) - << var; - - // FIXME: Add additional diagnostic info about class etc. which prevents - // capture. +bool Sema::tryCaptureVariable(ValueDecl *Var, SourceLocation Loc, + TryCaptureKind Kind, SourceLocation EllipsisLoc) { + QualType CaptureType; + QualType DeclRefType; + return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc, + /*BuildAndDiagnose=*/true, CaptureType, + DeclRefType, nullptr); } -static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, - ValueDecl *Var, - bool &SubCapturesAreNested, - QualType &CaptureType, - QualType &DeclRefType) { - // Check whether we've already captured it. - if (CSI->CaptureMap.count(Var)) { - // If we found a capture, any subcaptures are nested. - SubCapturesAreNested = true; +bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) { + QualType CaptureType; + QualType DeclRefType; + return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), + /*BuildAndDiagnose=*/false, CaptureType, + DeclRefType, nullptr); +} - // Retrieve the capture type for this variable. - CaptureType = CSI->getCapture(Var).getCaptureType(); +QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) { + QualType CaptureType; + QualType DeclRefType; - // Compute the type of an expression that refers to this variable. - DeclRefType = CaptureType.getNonReferenceType(); + // Determine whether we can capture this variable. + if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), + /*BuildAndDiagnose=*/false, CaptureType, + DeclRefType, nullptr)) + return QualType(); - // Similarly to mutable captures in lambda, all the OpenMP captures by copy - // are mutable in the sense that user can change their value - they are - // private instances of the captured declarations. - const Capture &Cap = CSI->getCapture(Var); - if (Cap.isCopyCapture() && - !(isa(CSI) && - !cast(CSI)->lambdaCaptureShouldBeConst()) && - !(isa(CSI) && - cast(CSI)->CapRegionKind == CR_OpenMP)) - DeclRefType.addConst(); - return true; - } - return false; + return DeclRefType; } -// Only block literals, captured statements, and lambda expressions can -// capture; other scopes don't work. -static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, - ValueDecl *Var, - SourceLocation Loc, - const bool Diagnose, - Sema &S) { - if (isa(DC) || isa(DC) || isLambdaCallOperator(DC)) - return getLambdaAwareParentOfDeclContext(DC); - - VarDecl *Underlying = Var->getPotentiallyDecomposedVarDecl(); - if (Underlying) { - if (Underlying->hasLocalStorage() && Diagnose) - diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); +namespace { +// Helper to copy the template arguments from a DeclRefExpr or MemberExpr. +// The produced TemplateArgumentListInfo* points to data stored within this +// object, so should only be used in contexts where the pointer will not be +// used after the CopiedTemplateArgs object is destroyed. +class CopiedTemplateArgs { + bool HasArgs; + TemplateArgumentListInfo TemplateArgStorage; +public: + template + CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) { + if (HasArgs) + E->copyTemplateArgumentsInto(TemplateArgStorage); } - return nullptr; + operator TemplateArgumentListInfo*() +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(clang::lifetimebound) + [[clang::lifetimebound]] +#endif +#endif + { + return HasArgs ? &TemplateArgStorage : nullptr; + } +}; } -// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture -// certain types of variables (unnamed, variably modified types etc.) -// so check for eligibility. -static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var, - SourceLocation Loc, const bool Diagnose, - Sema &S) { - - assert((isa(Var)) && - "Only variables and structured bindings can be captured"); +/// Walk the set of potential results of an expression and mark them all as +/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason. +/// +/// \return A new expression if we found any potential results, ExprEmpty() if +/// not, and ExprError() if we diagnosed an error. +static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, + NonOdrUseReason NOUR) { + // Per C++11 [basic.def.odr], a variable is odr-used "unless it is + // an object that satisfies the requirements for appearing in a + // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) + // is immediately applied." This function handles the lvalue-to-rvalue + // conversion part. + // + // If we encounter a node that claims to be an odr-use but shouldn't be, we + // transform it into the relevant kind of non-odr-use node and rebuild the + // tree of nodes leading to it. + // + // This is a mini-TreeTransform that only transforms a restricted subset of + // nodes (and only certain operands of them). - bool IsBlock = isa(CSI); - bool IsLambda = isa(CSI); + // Rebuild a subexpression. + auto Rebuild = [&](Expr *Sub) { + return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR); + }; - // Lambdas are not allowed to capture unnamed variables - // (e.g. anonymous unions). - // FIXME: The C++11 rule don't actually state this explicitly, but I'm - // assuming that's the intent. - if (IsLambda && !Var->getDeclName()) { - if (Diagnose) { - S.Diag(Loc, diag::err_lambda_capture_anonymous_var); - S.Diag(Var->getLocation(), diag::note_declared_at); - } - return false; - } + // Check whether a potential result satisfies the requirements of NOUR. + auto IsPotentialResultOdrUsed = [&](NamedDecl *D) { + // Any entity other than a VarDecl is always odr-used whenever it's named + // in a potentially-evaluated expression. + auto *VD = dyn_cast(D); + if (!VD) + return true; - // Prohibit variably-modified types in blocks; they're difficult to deal with. - if (Var->getType()->isVariablyModifiedType() && IsBlock) { - if (Diagnose) { - S.Diag(Loc, diag::err_ref_vm_type); - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } - return false; - } - // Prohibit structs with flexible array members too. - // We cannot capture what is in the tail end of the struct. - if (const RecordType *VTTy = Var->getType()->getAs()) { - if (VTTy->getDecl()->hasFlexibleArrayMember()) { - if (Diagnose) { - if (IsBlock) - S.Diag(Loc, diag::err_ref_flexarray_type); - else - S.Diag(Loc, diag::err_lambda_capture_flexarray_type) << Var; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } - return false; - } - } - const bool HasBlocksAttr = Var->hasAttr(); - // Lambdas and captured statements are not allowed to capture __block - // variables; they don't support the expected semantics. - if (HasBlocksAttr && (IsLambda || isa(CSI))) { - if (Diagnose) { - S.Diag(Loc, diag::err_capture_block_variable) << Var << !IsLambda; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } - return false; - } - // OpenCL v2.0 s6.12.5: Blocks cannot reference/capture other blocks - if (S.getLangOpts().OpenCL && IsBlock && - Var->getType()->isBlockPointerType()) { - if (Diagnose) - S.Diag(Loc, diag::err_opencl_block_ref_block); - return false; - } + // C++2a [basic.def.odr]p4: + // A variable x whose name appears as a potentially-evalauted expression + // e is odr-used by e unless + // -- x is a reference that is usable in constant expressions, or + // -- x is a variable of non-reference type that is usable in constant + // expressions and has no mutable subobjects, and e is an element of + // the set of potential results of an expression of + // non-volatile-qualified non-class type to which the lvalue-to-rvalue + // conversion is applied, or + // -- x is a variable of non-reference type, and e is an element of the + // set of potential results of a discarded-value expression to which + // the lvalue-to-rvalue conversion is not applied + // + // We check the first bullet and the "potentially-evaluated" condition in + // BuildDeclRefExpr. We check the type requirements in the second bullet + // in CheckLValueToRValueConversionOperand below. + switch (NOUR) { + case NOUR_None: + case NOUR_Unevaluated: + llvm_unreachable("unexpected non-odr-use-reason"); - if (isa(Var)) { - if (!IsLambda || !S.getLangOpts().CPlusPlus) { - if (Diagnose) - diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); - return false; - } else if (Diagnose && S.getLangOpts().CPlusPlus) { - S.Diag(Loc, S.LangOpts.CPlusPlus20 - ? diag::warn_cxx17_compat_capture_binding - : diag::ext_capture_binding) - << Var; - S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; + case NOUR_Constant: + // Constant references were handled when they were built. + if (VD->getType()->isReferenceType()) + return true; + if (auto *RD = VD->getType()->getAsCXXRecordDecl()) + if (RD->hasMutableFields()) + return true; + if (!VD->isUsableInConstantExpressions(S.Context)) + return true; + break; + + case NOUR_Discarded: + if (VD->getType()->isReferenceType()) + return true; + break; } - } + return false; + }; - return true; -} + // Mark that this expression does not constitute an odr-use. + auto MarkNotOdrUsed = [&] { + S.MaybeODRUseExprs.remove(E); + if (LambdaScopeInfo *LSI = S.getCurLambda()) + LSI->markVariableExprAsNonODRUsed(E); + }; -// Returns true if the capture by block was successful. -static bool captureInBlock(BlockScopeInfo *BSI, ValueDecl *Var, - SourceLocation Loc, const bool BuildAndDiagnose, - QualType &CaptureType, QualType &DeclRefType, - const bool Nested, Sema &S, bool Invalid) { - bool ByRef = false; + // C++2a [basic.def.odr]p2: + // The set of potential results of an expression e is defined as follows: + switch (E->getStmtClass()) { + // -- If e is an id-expression, ... + case Expr::DeclRefExprClass: { + auto *DRE = cast(E); + if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl())) + break; - // Blocks are not allowed to capture arrays, excepting OpenCL. - // OpenCL v2.0 s1.12.5 (revision 40): arrays are captured by reference - // (decayed to pointers). - if (!Invalid && !S.getLangOpts().OpenCL && CaptureType->isArrayType()) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_ref_array_type); - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Invalid = true; - } else { - return false; - } + // Rebuild as a non-odr-use DeclRefExpr. + MarkNotOdrUsed(); + return DeclRefExpr::Create( + S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(), + DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(), + DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(), + DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR); } - // Forbid the block-capture of autoreleasing variables. - if (!Invalid && - CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_arc_autoreleasing_capture) - << /*block*/ 0; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Invalid = true; - } else { - return false; - } + case Expr::FunctionParmPackExprClass: { + auto *FPPE = cast(E); + // If any of the declarations in the pack is odr-used, then the expression + // as a whole constitutes an odr-use. + for (VarDecl *D : *FPPE) + if (IsPotentialResultOdrUsed(D)) + return ExprEmpty(); + + // FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice, + // nothing cares about whether we marked this as an odr-use, but it might + // be useful for non-compiler tools. + MarkNotOdrUsed(); + break; } - // Warn about implicitly autoreleasing indirect parameters captured by blocks. - if (const auto *PT = CaptureType->getAs()) { - QualType PointeeTy = PT->getPointeeType(); + // -- If e is a subscripting operation with an array operand... + case Expr::ArraySubscriptExprClass: { + auto *ASE = cast(E); + Expr *OldBase = ASE->getBase()->IgnoreImplicit(); + if (!OldBase->getType()->isArrayType()) + break; + ExprResult Base = Rebuild(OldBase); + if (!Base.isUsable()) + return Base; + Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() : ASE->getLHS(); + Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() : ASE->getRHS(); + SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored. + return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS, + ASE->getRBracketLoc()); + } - if (!Invalid && PointeeTy->getAs() && - PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing && - !S.Context.hasDirectOwnershipQualifier(PointeeTy)) { - if (BuildAndDiagnose) { - SourceLocation VarLoc = Var->getLocation(); - S.Diag(Loc, diag::warn_block_capture_autoreleasing); - S.Diag(VarLoc, diag::note_declare_parameter_strong); - } + case Expr::MemberExprClass: { + auto *ME = cast(E); + // -- If e is a class member access expression [...] naming a non-static + // data member... + if (isa(ME->getMemberDecl())) { + ExprResult Base = Rebuild(ME->getBase()); + if (!Base.isUsable()) + return Base; + return MemberExpr::Create( + S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(), + ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), + ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(), + CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(), + ME->getObjectKind(), ME->isNonOdrUse()); } - } - const bool HasBlocksAttr = Var->hasAttr(); - if (HasBlocksAttr || CaptureType->isReferenceType() || - (S.getLangOpts().OpenMP && S.OpenMP().isOpenMPCapturedDecl(Var))) { - // Block capture by reference does not change the capture or - // declaration reference types. - ByRef = true; - } else { - // Block capture by copy introduces 'const'. - CaptureType = CaptureType.getNonReferenceType().withConst(); - DeclRefType = CaptureType; - } + if (ME->getMemberDecl()->isCXXInstanceMember()) + break; - // Actually capture the variable. - if (BuildAndDiagnose) - BSI->addCapture(Var, HasBlocksAttr, ByRef, Nested, Loc, SourceLocation(), - CaptureType, Invalid); + // -- If e is a class member access expression naming a static data member, + // ... + if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl())) + break; - return !Invalid; -} + // Rebuild as a non-odr-use MemberExpr. + MarkNotOdrUsed(); + return MemberExpr::Create( + S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(), + ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(), + ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME), + ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR); + } -/// Capture the given variable in the captured region. -static bool captureInCapturedRegion( - CapturedRegionScopeInfo *RSI, ValueDecl *Var, SourceLocation Loc, - const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, - const bool RefersToCapturedVariable, Sema::TryCaptureKind Kind, - bool IsTopScope, Sema &S, bool Invalid) { - // By default, capture variables by reference. - bool ByRef = true; - if (IsTopScope && Kind != Sema::TryCapture_Implicit) { - ByRef = (Kind == Sema::TryCapture_ExplicitByRef); - } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) { - // Using an LValue reference type is consistent with Lambdas (see below). - if (S.OpenMP().isOpenMPCapturedDecl(Var)) { - bool HasConst = DeclRefType.isConstQualified(); - DeclRefType = DeclRefType.getUnqualifiedType(); - // Don't lose diagnostics about assignments to const. - if (HasConst) - DeclRefType.addConst(); + case Expr::BinaryOperatorClass: { + auto *BO = cast(E); + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + // -- If e is a pointer-to-member expression of the form e1 .* e2 ... + if (BO->getOpcode() == BO_PtrMemD) { + ExprResult Sub = Rebuild(LHS); + if (!Sub.isUsable()) + return Sub; + BO->setLHS(Sub.get()); + // -- If e is a comma expression, ... + } else if (BO->getOpcode() == BO_Comma) { + ExprResult Sub = Rebuild(RHS); + if (!Sub.isUsable()) + return Sub; + BO->setRHS(Sub.get()); + } else { + break; } - // Do not capture firstprivates in tasks. - if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel) != OMPC_unknown) - return true; - ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel); + return ExprResult(BO); } - if (ByRef) - CaptureType = S.Context.getLValueReferenceType(DeclRefType); - else - CaptureType = DeclRefType; - - // Actually capture the variable. - if (BuildAndDiagnose) - RSI->addCapture(Var, /*isBlock*/ false, ByRef, RefersToCapturedVariable, - Loc, SourceLocation(), CaptureType, Invalid); - - return !Invalid; -} + // -- If e has the form (e1)... + case Expr::ParenExprClass: { + auto *PE = cast(E); + ExprResult Sub = Rebuild(PE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get()); + } -/// Capture the given variable in the lambda. -static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, - SourceLocation Loc, const bool BuildAndDiagnose, - QualType &CaptureType, QualType &DeclRefType, - const bool RefersToCapturedVariable, - const Sema::TryCaptureKind Kind, - SourceLocation EllipsisLoc, const bool IsTopScope, - Sema &S, bool Invalid) { - // Determine whether we are capturing by reference or by value. - bool ByRef = false; - if (IsTopScope && Kind != Sema::TryCapture_Implicit) { - ByRef = (Kind == Sema::TryCapture_ExplicitByRef); - } else { - ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref); + // -- If e is a glvalue conditional expression, ... + // We don't apply this to a binary conditional operator. FIXME: Should we? + case Expr::ConditionalOperatorClass: { + auto *CO = cast(E); + ExprResult LHS = Rebuild(CO->getLHS()); + if (LHS.isInvalid()) + return ExprError(); + ExprResult RHS = Rebuild(CO->getRHS()); + if (RHS.isInvalid()) + return ExprError(); + if (!LHS.isUsable() && !RHS.isUsable()) + return ExprEmpty(); + if (!LHS.isUsable()) + LHS = CO->getLHS(); + if (!RHS.isUsable()) + RHS = CO->getRHS(); + return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(), + CO->getCond(), LHS.get(), RHS.get()); } - if (BuildAndDiagnose && S.Context.getTargetInfo().getTriple().isWasm() && - CaptureType.getNonReferenceType().isWebAssemblyReferenceType()) { - S.Diag(Loc, diag::err_wasm_ca_reference) << 0; - Invalid = true; + // [Clang extension] + // -- If e has the form __extension__ e1... + case Expr::UnaryOperatorClass: { + auto *UO = cast(E); + if (UO->getOpcode() != UO_Extension) + break; + ExprResult Sub = Rebuild(UO->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension, + Sub.get()); } - // Compute the type of the field that will capture this variable. - if (ByRef) { - // C++11 [expr.prim.lambda]p15: - // An entity is captured by reference if it is implicitly or - // explicitly captured but not captured by copy. It is - // unspecified whether additional unnamed non-static data - // members are declared in the closure type for entities - // captured by reference. - // - // FIXME: It is not clear whether we want to build an lvalue reference - // to the DeclRefType or to CaptureType.getNonReferenceType(). GCC appears - // to do the former, while EDG does the latter. Core issue 1249 will - // clarify, but for now we follow GCC because it's a more permissive and - // easily defensible position. - CaptureType = S.Context.getLValueReferenceType(DeclRefType); - } else { - // C++11 [expr.prim.lambda]p14: - // For each entity captured by copy, an unnamed non-static - // data member is declared in the closure type. The - // declaration order of these members is unspecified. The type - // of such a data member is the type of the corresponding - // captured entity if the entity is not a reference to an - // object, or the referenced type otherwise. [Note: If the - // captured entity is a reference to a function, the - // corresponding data member is also a reference to a - // function. - end note ] - if (const ReferenceType *RefType = CaptureType->getAs()){ - if (!RefType->getPointeeType()->isFunctionType()) - CaptureType = RefType->getPointeeType(); - } + // [Clang extension] + // -- If e has the form _Generic(...), the set of potential results is the + // union of the sets of potential results of the associated expressions. + case Expr::GenericSelectionExprClass: { + auto *GSE = cast(E); - // Forbid the lambda copy-capture of autoreleasing variables. - if (!Invalid && - CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_arc_autoreleasing_capture) << /*lambda*/ 1; - S.Diag(Var->getLocation(), diag::note_previous_decl) - << Var->getDeclName(); - Invalid = true; + SmallVector AssocExprs; + bool AnyChanged = false; + for (Expr *OrigAssocExpr : GSE->getAssocExprs()) { + ExprResult AssocExpr = Rebuild(OrigAssocExpr); + if (AssocExpr.isInvalid()) + return ExprError(); + if (AssocExpr.isUsable()) { + AssocExprs.push_back(AssocExpr.get()); + AnyChanged = true; } else { - return false; + AssocExprs.push_back(OrigAssocExpr); } } - // Make sure that by-copy captures are of a complete and non-abstract type. - if (!Invalid && BuildAndDiagnose) { - if (!CaptureType->isDependentType() && - S.RequireCompleteSizedType( - Loc, CaptureType, - diag::err_capture_of_incomplete_or_sizeless_type, - Var->getDeclName())) - Invalid = true; - else if (S.RequireNonAbstractType(Loc, CaptureType, - diag::err_capture_of_abstract_type)) - Invalid = true; - } + void *ExOrTy = nullptr; + bool IsExpr = GSE->isExprPredicate(); + if (IsExpr) + ExOrTy = GSE->getControllingExpr(); + else + ExOrTy = GSE->getControllingType(); + return AnyChanged ? S.CreateGenericSelectionExpr( + GSE->getGenericLoc(), GSE->getDefaultLoc(), + GSE->getRParenLoc(), IsExpr, ExOrTy, + GSE->getAssocTypeSourceInfos(), AssocExprs) + : ExprEmpty(); } - // Compute the type of a reference to this captured variable. - if (ByRef) - DeclRefType = CaptureType.getNonReferenceType(); - else { - // C++ [expr.prim.lambda]p5: - // The closure type for a lambda-expression has a public inline - // function call operator [...]. This function call operator is - // declared const (9.3.1) if and only if the lambda-expression's - // parameter-declaration-clause is not followed by mutable. - DeclRefType = CaptureType.getNonReferenceType(); - bool Const = LSI->lambdaCaptureShouldBeConst(); - if (Const && !CaptureType->isReferenceType()) - DeclRefType.addConst(); - } + // [Clang extension] + // -- If e has the form __builtin_choose_expr(...), the set of potential + // results is the union of the sets of potential results of the + // second and third subexpressions. + case Expr::ChooseExprClass: { + auto *CE = cast(E); - // Add the capture. - if (BuildAndDiagnose) - LSI->addCapture(Var, /*isBlock=*/false, ByRef, RefersToCapturedVariable, - Loc, EllipsisLoc, CaptureType, Invalid); + ExprResult LHS = Rebuild(CE->getLHS()); + if (LHS.isInvalid()) + return ExprError(); - return !Invalid; -} + ExprResult RHS = Rebuild(CE->getLHS()); + if (RHS.isInvalid()) + return ExprError(); -static bool canCaptureVariableByCopy(ValueDecl *Var, - const ASTContext &Context) { - // Offer a Copy fix even if the type is dependent. - if (Var->getType()->isDependentType()) - return true; - QualType T = Var->getType().getNonReferenceType(); - if (T.isTriviallyCopyableType(Context)) - return true; - if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { + if (!LHS.get() && !RHS.get()) + return ExprEmpty(); + if (!LHS.isUsable()) + LHS = CE->getLHS(); + if (!RHS.isUsable()) + RHS = CE->getRHS(); - if (!(RD = RD->getDefinition())) - return false; - if (RD->hasSimpleCopyConstructor()) - return true; - if (RD->hasUserDeclaredCopyConstructor()) - for (CXXConstructorDecl *Ctor : RD->ctors()) - if (Ctor->isCopyConstructor()) - return !Ctor->isDeleted(); + return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(), + RHS.get(), CE->getRParenLoc()); } - return false; -} - -/// Create up to 4 fix-its for explicit reference and value capture of \p Var or -/// default capture. Fixes may be omitted if they aren't allowed by the -/// standard, for example we can't emit a default copy capture fix-it if we -/// already explicitly copy capture capture another variable. -static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI, - ValueDecl *Var) { - assert(LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None); - // Don't offer Capture by copy of default capture by copy fixes if Var is - // known not to be copy constructible. - bool ShouldOfferCopyFix = canCaptureVariableByCopy(Var, Sema.getASTContext()); - SmallString<32> FixBuffer; - StringRef Separator = LSI->NumExplicitCaptures > 0 ? ", " : ""; - if (Var->getDeclName().isIdentifier() && !Var->getName().empty()) { - SourceLocation VarInsertLoc = LSI->IntroducerRange.getEnd(); - if (ShouldOfferCopyFix) { - // Offer fixes to insert an explicit capture for the variable. - // [] -> [VarName] - // [OtherCapture] -> [OtherCapture, VarName] - FixBuffer.assign({Separator, Var->getName()}); - Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) - << Var << /*value*/ 0 - << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); - } - // As above but capture by reference. - FixBuffer.assign({Separator, "&", Var->getName()}); - Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) - << Var << /*reference*/ 1 - << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + // Step through non-syntactic nodes. + case Expr::ConstantExprClass: { + auto *CE = cast(E); + ExprResult Sub = Rebuild(CE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return ConstantExpr::Create(S.Context, Sub.get()); } - // Only try to offer default capture if there are no captures excluding this - // and init captures. - // [this]: OK. - // [X = Y]: OK. - // [&A, &B]: Don't offer. - // [A, B]: Don't offer. - if (llvm::any_of(LSI->Captures, [](Capture &C) { - return !C.isThisCapture() && !C.isInitCapture(); - })) - return; - - // The default capture specifiers, '=' or '&', must appear first in the - // capture body. - SourceLocation DefaultInsertLoc = - LSI->IntroducerRange.getBegin().getLocWithOffset(1); - - if (ShouldOfferCopyFix) { - bool CanDefaultCopyCapture = true; - // [=, *this] OK since c++17 - // [=, this] OK since c++20 - if (LSI->isCXXThisCaptured() && !Sema.getLangOpts().CPlusPlus20) - CanDefaultCopyCapture = Sema.getLangOpts().CPlusPlus17 - ? LSI->getCXXThisCapture().isCopyCapture() - : false; - // We can't use default capture by copy if any captures already specified - // capture by copy. - if (CanDefaultCopyCapture && llvm::none_of(LSI->Captures, [](Capture &C) { - return !C.isThisCapture() && !C.isInitCapture() && C.isCopyCapture(); - })) { - FixBuffer.assign({"=", Separator}); - Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) - << /*value*/ 0 - << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + // We could mostly rely on the recursive rebuilding to rebuild implicit + // casts, but not at the top level, so rebuild them here. + case Expr::ImplicitCastExprClass: { + auto *ICE = cast(E); + // Only step through the narrow set of cast kinds we expect to encounter. + // Anything else suggests we've left the region in which potential results + // can be found. + switch (ICE->getCastKind()) { + case CK_NoOp: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + ExprResult Sub = Rebuild(ICE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + CXXCastPath Path(ICE->path()); + return S.ImpCastExprToType(Sub.get(), ICE->getType(), ICE->getCastKind(), + ICE->getValueKind(), &Path); } - } - // We can't use default capture by reference if any captures already specified - // capture by reference. - if (llvm::none_of(LSI->Captures, [](Capture &C) { - return !C.isInitCapture() && C.isReferenceCapture() && - !C.isThisCapture(); - })) { - FixBuffer.assign({"&", Separator}); - Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) - << /*reference*/ 1 - << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + default: + break; + } + break; } + + default: + break; + } + + // Can't traverse through this node. Nothing to do. + return ExprEmpty(); } -bool Sema::tryCaptureVariable( - ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, - SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, - QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { - // An init-capture is notionally from the context surrounding its - // declaration, but its parent DC is the lambda class. - DeclContext *VarDC = Var->getDeclContext(); - DeclContext *DC = CurContext; +ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { + // Check whether the operand is or contains an object of non-trivial C union + // type. + if (E->getType().isVolatileQualified() && + (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion())) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + Sema::NTCUC_LValueToRValueVolatile, + NTCUK_Destruct|NTCUK_Copy); - // Skip past RequiresExprBodys because they don't constitute function scopes. - while (DC->isRequiresExprBody()) - DC = DC->getParent(); + // C++2a [basic.def.odr]p4: + // [...] an expression of non-volatile-qualified non-class type to which + // the lvalue-to-rvalue conversion is applied [...] + if (E->getType().isVolatileQualified() || E->getType()->getAs()) + return E; - // tryCaptureVariable is called every time a DeclRef is formed, - // it can therefore have non-negigible impact on performances. - // For local variables and when there is no capturing scope, - // we can bailout early. - if (CapturingFunctionScopes == 0 && (!BuildAndDiagnose || VarDC == DC)) - return true; + ExprResult Result = + rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant); + if (Result.isInvalid()) + return ExprError(); + return Result.get() ? Result : E; +} - // Exception: Function parameters are not tied to the function's DeclContext - // until we enter the function definition. Capturing them anyway would result - // in an out-of-bounds error while traversing DC and its parents. - if (isa(Var) && !VarDC->isFunctionOrMethod()) - return true; +ExprResult Sema::ActOnConstantExpression(ExprResult Res) { + Res = CorrectDelayedTyposInExpr(Res); - const auto *VD = dyn_cast(Var); - if (VD) { - if (VD->isInitCapture()) - VarDC = VarDC->getParent(); - } else { - VD = Var->getPotentiallyDecomposedVarDecl(); - } - assert(VD && "Cannot capture a null variable"); + if (!Res.isUsable()) + return Res; - const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt - ? *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; - // We need to sync up the Declaration Context with the - // FunctionScopeIndexToStopAt - if (FunctionScopeIndexToStopAt) { - unsigned FSIndex = FunctionScopes.size() - 1; - while (FSIndex != MaxFunctionScopesIndex) { - DC = getLambdaAwareParentOfDeclContext(DC); - --FSIndex; + // If a constant-expression is a reference to a variable where we delay + // deciding whether it is an odr-use, just assume we will apply the + // lvalue-to-rvalue conversion. In the one case where this doesn't happen + // (a non-type template argument), we have special handling anyway. + return CheckLValueToRValueConversionOperand(Res.get()); +} + +void Sema::CleanupVarDeclMarking() { + // Iterate through a local copy in case MarkVarDeclODRUsed makes a recursive + // call. + MaybeODRUseExprSet LocalMaybeODRUseExprs; + std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs); + + for (Expr *E : LocalMaybeODRUseExprs) { + if (auto *DRE = dyn_cast(E)) { + MarkVarDeclODRUsed(cast(DRE->getDecl()), + DRE->getLocation(), *this); + } else if (auto *ME = dyn_cast(E)) { + MarkVarDeclODRUsed(cast(ME->getMemberDecl()), ME->getMemberLoc(), + *this); + } else if (auto *FP = dyn_cast(E)) { + for (VarDecl *VD : *FP) + MarkVarDeclODRUsed(VD, FP->getParameterPackLocation(), *this); + } else { + llvm_unreachable("Unexpected expression"); } } - // Capture global variables if it is required to use private copy of this - // variable. - bool IsGlobal = !VD->hasLocalStorage(); - if (IsGlobal && !(LangOpts.OpenMP && - OpenMP().isOpenMPCapturedDecl(Var, /*CheckScopeInfo=*/true, - MaxFunctionScopesIndex))) - return true; + assert(MaybeODRUseExprs.empty() && + "MarkVarDeclODRUsed failed to cleanup MaybeODRUseExprs?"); +} - if (isa(Var)) - Var = cast(Var->getCanonicalDecl()); +static void DoMarkPotentialCapture(Sema &SemaRef, SourceLocation Loc, + ValueDecl *Var, Expr *E) { + VarDecl *VD = Var->getPotentiallyDecomposedVarDecl(); + if (!VD) + return; - // Walk up the stack to determine whether we can capture the variable, - // performing the "simple" checks that don't depend on type. We stop when - // we've either hit the declared scope of the variable or find an existing - // capture of that variable. We start from the innermost capturing-entity - // (the DC) and ensure that all intervening capturing-entities - // (blocks/lambdas etc.) between the innermost capturer and the variable`s - // declcontext can either capture the variable or have already captured - // the variable. - CaptureType = Var->getType(); - DeclRefType = CaptureType.getNonReferenceType(); - bool Nested = false; - bool Explicit = (Kind != TryCapture_Implicit); - unsigned FunctionScopesIndex = MaxFunctionScopesIndex; - do { + const bool RefersToEnclosingScope = + (SemaRef.CurContext != VD->getDeclContext() && + VD->getDeclContext()->isFunctionOrMethod() && VD->hasLocalStorage()); + if (RefersToEnclosingScope) { + LambdaScopeInfo *const LSI = + SemaRef.getCurLambda(/*IgnoreNonLambdaCapturingScope=*/true); + if (LSI && (!LSI->CallOperator || + !LSI->CallOperator->Encloses(Var->getDeclContext()))) { + // If a variable could potentially be odr-used, defer marking it so + // until we finish analyzing the full expression for any + // lvalue-to-rvalue + // or discarded value conversions that would obviate odr-use. + // Add it to the list of potential captures that will be analyzed + // later (ActOnFinishFullExpr) for eventual capture and odr-use marking + // unless the variable is a reference that was initialized by a constant + // expression (this will never need to be captured or odr-used). + // + // FIXME: We can simplify this a lot after implementing P0588R1. + assert(E && "Capture variable should be used in an expression."); + if (!Var->getType()->isReferenceType() || + !VD->isUsableInConstantExpressions(SemaRef.Context)) + LSI->addPotentialCapture(E->IgnoreParens()); + } + } +} - LambdaScopeInfo *LSI = nullptr; - if (!FunctionScopes.empty()) - LSI = dyn_cast_or_null( - FunctionScopes[FunctionScopesIndex]); +static void DoMarkVarDeclReferenced( + Sema &SemaRef, SourceLocation Loc, VarDecl *Var, Expr *E, + llvm::DenseMap &RefsMinusAssignments) { + assert((!E || isa(E) || isa(E) || + isa(E)) && + "Invalid Expr argument to DoMarkVarDeclReferenced"); + Var->setReferenced(); - bool IsInScopeDeclarationContext = - !LSI || LSI->AfterParameterList || CurContext == LSI->CallOperator; + if (Var->isInvalidDecl()) + return; - if (LSI && !LSI->AfterParameterList) { - // This allows capturing parameters from a default value which does not - // seems correct - if (isa(Var) && !Var->getDeclContext()->isFunctionOrMethod()) - return true; - } - // If the variable is declared in the current context, there is no need to - // capture it. - if (IsInScopeDeclarationContext && - FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC) - return true; + auto *MSI = Var->getMemberSpecializationInfo(); + TemplateSpecializationKind TSK = MSI ? MSI->getTemplateSpecializationKind() + : Var->getTemplateSpecializationKind(); - // Only block literals, captured statements, and lambda expressions can - // capture; other scopes don't work. - DeclContext *ParentDC = - !IsInScopeDeclarationContext - ? DC->getParent() - : getParentOfCapturingContextOrNull(DC, Var, ExprLoc, - BuildAndDiagnose, *this); - // We need to check for the parent *first* because, if we *have* - // private-captured a global variable, we need to recursively capture it in - // intermediate blocks, lambdas, etc. - if (!ParentDC) { - if (IsGlobal) { - FunctionScopesIndex = MaxFunctionScopesIndex - 1; - break; - } - return true; - } + OdrUseContext OdrUse = isOdrUseContext(SemaRef); + bool UsableInConstantExpr = + Var->mightBeUsableInConstantExpressions(SemaRef.Context); - FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; - CapturingScopeInfo *CSI = cast(FSI); + if (Var->isLocalVarDeclOrParm() && !Var->hasExternalStorage()) { + RefsMinusAssignments.insert({Var, 0}).first->getSecond()++; + } - // Check whether we've already captured it. - if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, - DeclRefType)) { - CSI->getCapture(Var).markUsed(BuildAndDiagnose); - break; - } + // C++20 [expr.const]p12: + // A variable [...] is needed for constant evaluation if it is [...] a + // variable whose name appears as a potentially constant evaluated + // expression that is either a contexpr variable or is of non-volatile + // const-qualified integral type or of reference type + bool NeededForConstantEvaluation = + isPotentiallyConstantEvaluatedContext(SemaRef) && UsableInConstantExpr; - // When evaluating some attributes (like enable_if) we might refer to a - // function parameter appertaining to the same declaration as that - // attribute. - if (const auto *Parm = dyn_cast(Var); - Parm && Parm->getDeclContext() == DC) - return true; + bool NeedDefinition = + OdrUse == OdrUseContext::Used || NeededForConstantEvaluation; - // If we are instantiating a generic lambda call operator body, - // we do not want to capture new variables. What was captured - // during either a lambdas transformation or initial parsing - // should be used. - if (isGenericLambdaCallOperatorSpecialization(DC)) { - if (BuildAndDiagnose) { - LambdaScopeInfo *LSI = cast(CSI); - if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { - Diag(ExprLoc, diag::err_lambda_impcap) << Var; - Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); - buildLambdaCaptureFixit(*this, LSI, Var); - } else - diagnoseUncapturableValueReferenceOrBinding(*this, ExprLoc, Var); - } - return true; - } + assert(!isa(Var) && + "Can't instantiate a partial template specialization."); - // Try to capture variable-length arrays types. - if (Var->getType()->isVariablyModifiedType()) { - // We're going to walk down into the type and look for VLA - // expressions. - QualType QTy = Var->getType(); - if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) - QTy = PVD->getOriginalType(); - captureVariablyModifiedType(Context, QTy, CSI); - } + // If this might be a member specialization of a static data member, check + // the specialization is visible. We already did the checks for variable + // template specializations when we created them. + if (NeedDefinition && TSK != TSK_Undeclared && + !isa(Var)) + SemaRef.checkSpecializationVisibility(Loc, Var); - if (getLangOpts().OpenMP) { - if (auto *RSI = dyn_cast(CSI)) { - // OpenMP private variables should not be captured in outer scope, so - // just break here. Similarly, global variables that are captured in a - // target region should not be captured outside the scope of the region. - if (RSI->CapRegionKind == CR_OpenMP) { - // FIXME: We should support capturing structured bindings in OpenMP. - if (isa(Var)) { - if (BuildAndDiagnose) { - Diag(ExprLoc, diag::err_capture_binding_openmp) << Var; - Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; - } - return true; - } - OpenMPClauseKind IsOpenMPPrivateDecl = OpenMP().isOpenMPPrivateDecl( - Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); - // If the variable is private (i.e. not captured) and has variably - // modified type, we still need to capture the type for correct - // codegen in all regions, associated with the construct. Currently, - // it is captured in the innermost captured region only. - if (IsOpenMPPrivateDecl != OMPC_unknown && - Var->getType()->isVariablyModifiedType()) { - QualType QTy = Var->getType(); - if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) - QTy = PVD->getOriginalType(); - for (int I = 1, - E = OpenMP().getNumberOfConstructScopes(RSI->OpenMPLevel); - I < E; ++I) { - auto *OuterRSI = cast( - FunctionScopes[FunctionScopesIndex - I]); - assert(RSI->OpenMPLevel == OuterRSI->OpenMPLevel && - "Wrong number of captured regions associated with the " - "OpenMP construct."); - captureVariablyModifiedType(Context, QTy, OuterRSI); - } - } - bool IsTargetCap = - IsOpenMPPrivateDecl != OMPC_private && - OpenMP().isOpenMPTargetCapturedDecl(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel); - // Do not capture global if it is not privatized in outer regions. - bool IsGlobalCap = - IsGlobal && OpenMP().isOpenMPGlobalCapturedDecl( - Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); + // Perform implicit instantiation of static data members, static data member + // templates of class templates, and variable template specializations. Delay + // instantiations of variable templates, except for those that could be used + // in a constant expression. + if (NeedDefinition && isTemplateInstantiation(TSK)) { + // Per C++17 [temp.explicit]p10, we may instantiate despite an explicit + // instantiation declaration if a variable is usable in a constant + // expression (among other cases). + bool TryInstantiating = + TSK == TSK_ImplicitInstantiation || + (TSK == TSK_ExplicitInstantiationDeclaration && UsableInConstantExpr); - // When we detect target captures we are looking from inside the - // target region, therefore we need to propagate the capture from the - // enclosing region. Therefore, the capture is not initially nested. - if (IsTargetCap) - OpenMP().adjustOpenMPTargetScopeIndex(FunctionScopesIndex, - RSI->OpenMPLevel); + if (TryInstantiating) { + SourceLocation PointOfInstantiation = + MSI ? MSI->getPointOfInstantiation() : Var->getPointOfInstantiation(); + bool FirstInstantiation = PointOfInstantiation.isInvalid(); + if (FirstInstantiation) { + PointOfInstantiation = Loc; + if (MSI) + MSI->setPointOfInstantiation(PointOfInstantiation); + // FIXME: Notify listener. + else + Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); + } - if (IsTargetCap || IsOpenMPPrivateDecl == OMPC_private || - (IsGlobal && !IsGlobalCap)) { - Nested = !IsTargetCap; - bool HasConst = DeclRefType.isConstQualified(); - DeclRefType = DeclRefType.getUnqualifiedType(); - // Don't lose diagnostics about assignments to const. - if (HasConst) - DeclRefType.addConst(); - CaptureType = Context.getLValueReferenceType(DeclRefType); + if (UsableInConstantExpr) { + // Do not defer instantiations of variables that could be used in a + // constant expression. + SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] { + SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); + }); + + // Re-set the member to trigger a recomputation of the dependence bits + // for the expression. + if (auto *DRE = dyn_cast_or_null(E)) + DRE->setDecl(DRE->getDecl()); + else if (auto *ME = dyn_cast_or_null(E)) + ME->setMemberDecl(ME->getMemberDecl()); + } else if (FirstInstantiation) { + SemaRef.PendingInstantiations + .push_back(std::make_pair(Var, PointOfInstantiation)); + } else { + bool Inserted = false; + for (auto &I : SemaRef.SavedPendingInstantiations) { + auto Iter = llvm::find_if( + I, [Var](const Sema::PendingImplicitInstantiation &P) { + return P.first == Var; + }); + if (Iter != I.end()) { + SemaRef.PendingInstantiations.push_back(*Iter); + I.erase(Iter); + Inserted = true; break; } } + + // FIXME: For a specialization of a variable template, we don't + // distinguish between "declaration and type implicitly instantiated" + // and "implicit instantiation of definition requested", so we have + // no direct way to avoid enqueueing the pending instantiation + // multiple times. + if (isa(Var) && !Inserted) + SemaRef.PendingInstantiations + .push_back(std::make_pair(Var, PointOfInstantiation)); } } - if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { - // No capture-default, and this is not an explicit capture - // so cannot capture this variable. - if (BuildAndDiagnose) { - Diag(ExprLoc, diag::err_lambda_impcap) << Var; - Diag(Var->getLocation(), diag::note_previous_decl) << Var; - auto *LSI = cast(CSI); - if (LSI->Lambda) { - Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); - buildLambdaCaptureFixit(*this, LSI, Var); - } - // FIXME: If we error out because an outer lambda can not implicitly - // capture a variable that an inner lambda explicitly captures, we - // should have the inner lambda do the explicit capture - because - // it makes for cleaner diagnostics later. This would purely be done - // so that the diagnostic does not misleadingly claim that a variable - // can not be captured by a lambda implicitly even though it is captured - // explicitly. Suggestion: - // - create const bool VariableCaptureWasInitiallyExplicit = Explicit - // at the function head - // - cache the StartingDeclContext - this must be a lambda - // - captureInLambda in the innermost lambda the variable. - } - return true; - } - Explicit = false; - FunctionScopesIndex--; - if (IsInScopeDeclarationContext) - DC = ParentDC; - } while (!VarDC->Equals(DC)); - - // Walk back down the scope stack, (e.g. from outer lambda to inner lambda) - // computing the type of the capture at each step, checking type-specific - // requirements, and adding captures if requested. - // If the variable had already been captured previously, we start capturing - // at the lambda nested within that one. - bool Invalid = false; - for (unsigned I = ++FunctionScopesIndex, N = MaxFunctionScopesIndex + 1; I != N; - ++I) { - CapturingScopeInfo *CSI = cast(FunctionScopes[I]); + } - // Certain capturing entities (lambdas, blocks etc.) are not allowed to capture - // certain types of variables (unnamed, variably modified types etc.) - // so check for eligibility. - if (!Invalid) - Invalid = - !isVariableCapturable(CSI, Var, ExprLoc, BuildAndDiagnose, *this); + // C++2a [basic.def.odr]p4: + // A variable x whose name appears as a potentially-evaluated expression e + // is odr-used by e unless + // -- x is a reference that is usable in constant expressions + // -- x is a variable of non-reference type that is usable in constant + // expressions and has no mutable subobjects [FIXME], and e is an + // element of the set of potential results of an expression of + // non-volatile-qualified non-class type to which the lvalue-to-rvalue + // conversion is applied + // -- x is a variable of non-reference type, and e is an element of the set + // of potential results of a discarded-value expression to which the + // lvalue-to-rvalue conversion is not applied [FIXME] + // + // We check the first part of the second bullet here, and + // Sema::CheckLValueToRValueConversionOperand deals with the second part. + // FIXME: To get the third bullet right, we need to delay this even for + // variables that are not usable in constant expressions. - // After encountering an error, if we're actually supposed to capture, keep - // capturing in nested contexts to suppress any follow-on diagnostics. - if (Invalid && !BuildAndDiagnose) - return true; + // If we already know this isn't an odr-use, there's nothing more to do. + if (DeclRefExpr *DRE = dyn_cast_or_null(E)) + if (DRE->isNonOdrUse()) + return; + if (MemberExpr *ME = dyn_cast_or_null(E)) + if (ME->isNonOdrUse()) + return; - if (BlockScopeInfo *BSI = dyn_cast(CSI)) { - Invalid = !captureInBlock(BSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, - DeclRefType, Nested, *this, Invalid); - Nested = true; - } else if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { - Invalid = !captureInCapturedRegion( - RSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, - Kind, /*IsTopScope*/ I == N - 1, *this, Invalid); - Nested = true; - } else { - LambdaScopeInfo *LSI = cast(CSI); - Invalid = - !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, - DeclRefType, Nested, Kind, EllipsisLoc, - /*IsTopScope*/ I == N - 1, *this, Invalid); - Nested = true; - } + switch (OdrUse) { + case OdrUseContext::None: + // In some cases, a variable may not have been marked unevaluated, if it + // appears in a defaukt initializer. + assert((!E || isa(E) || + SemaRef.isUnevaluatedContext()) && + "missing non-odr-use marking for unevaluated decl ref"); + break; - if (Invalid && !BuildAndDiagnose) - return true; - } - return Invalid; -} + case OdrUseContext::FormallyOdrUsed: + // FIXME: Ignoring formal odr-uses results in incorrect lambda capture + // behavior. + break; -bool Sema::tryCaptureVariable(ValueDecl *Var, SourceLocation Loc, - TryCaptureKind Kind, SourceLocation EllipsisLoc) { - QualType CaptureType; - QualType DeclRefType; - return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc, - /*BuildAndDiagnose=*/true, CaptureType, - DeclRefType, nullptr); -} + case OdrUseContext::Used: + // If we might later find that this expression isn't actually an odr-use, + // delay the marking. + if (E && Var->isUsableInConstantExpressions(SemaRef.Context)) + SemaRef.MaybeODRUseExprs.insert(E); + else + MarkVarDeclODRUsed(Var, Loc, SemaRef); + break; -bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) { - QualType CaptureType; - QualType DeclRefType; - return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), - /*BuildAndDiagnose=*/false, CaptureType, - DeclRefType, nullptr); + case OdrUseContext::Dependent: + // If this is a dependent context, we don't need to mark variables as + // odr-used, but we may still need to track them for lambda capture. + // FIXME: Do we also need to do this inside dependent typeid expressions + // (which are modeled as unevaluated at this point)? + DoMarkPotentialCapture(SemaRef, Loc, Var, E); + break; + } } -QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) { - QualType CaptureType; - QualType DeclRefType; +static void DoMarkBindingDeclReferenced(Sema &SemaRef, SourceLocation Loc, + BindingDecl *BD, Expr *E) { + BD->setReferenced(); - // Determine whether we can capture this variable. - if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), - /*BuildAndDiagnose=*/false, CaptureType, - DeclRefType, nullptr)) - return QualType(); + if (BD->isInvalidDecl()) + return; - return DeclRefType; + OdrUseContext OdrUse = isOdrUseContext(SemaRef); + if (OdrUse == OdrUseContext::Used) { + QualType CaptureType, DeclRefType; + SemaRef.tryCaptureVariable(BD, Loc, Sema::TryCapture_Implicit, + /*EllipsisLoc*/ SourceLocation(), + /*BuildAndDiagnose*/ true, CaptureType, + DeclRefType, + /*FunctionScopeIndexToStopAt*/ nullptr); + } else if (OdrUse == OdrUseContext::Dependent) { + DoMarkPotentialCapture(SemaRef, Loc, BD, E); + } } -namespace { -// Helper to copy the template arguments from a DeclRefExpr or MemberExpr. -// The produced TemplateArgumentListInfo* points to data stored within this -// object, so should only be used in contexts where the pointer will not be -// used after the CopiedTemplateArgs object is destroyed. -class CopiedTemplateArgs { - bool HasArgs; - TemplateArgumentListInfo TemplateArgStorage; -public: - template - CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) { - if (HasArgs) - E->copyTemplateArgumentsInto(TemplateArgStorage); - } - operator TemplateArgumentListInfo*() -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(clang::lifetimebound) - [[clang::lifetimebound]] -#endif -#endif - { - return HasArgs ? &TemplateArgStorage : nullptr; - } -}; +void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { + DoMarkVarDeclReferenced(*this, Loc, Var, nullptr, RefsMinusAssignments); } -/// Walk the set of potential results of an expression and mark them all as -/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason. -/// -/// \return A new expression if we found any potential results, ExprEmpty() if -/// not, and ExprError() if we diagnosed an error. -static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, - NonOdrUseReason NOUR) { - // Per C++11 [basic.def.odr], a variable is odr-used "unless it is - // an object that satisfies the requirements for appearing in a - // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) - // is immediately applied." This function handles the lvalue-to-rvalue - // conversion part. - // - // If we encounter a node that claims to be an odr-use but shouldn't be, we - // transform it into the relevant kind of non-odr-use node and rebuild the - // tree of nodes leading to it. - // - // This is a mini-TreeTransform that only transforms a restricted subset of - // nodes (and only certain operands of them). +// C++ [temp.dep.expr]p3: +// An id-expression is type-dependent if it contains: +// - an identifier associated by name lookup with an entity captured by copy +// in a lambda-expression that has an explicit object parameter whose type +// is dependent ([dcl.fct]), +static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter( + Sema &SemaRef, ValueDecl *D, Expr *E) { + auto *ID = dyn_cast(E); + if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture()) + return; - // Rebuild a subexpression. - auto Rebuild = [&](Expr *Sub) { - return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR); - }; + // If any enclosing lambda with a dependent explicit object parameter either + // explicitly captures the variable by value, or has a capture default of '=' + // and does not capture the variable by reference, then the type of the DRE + // is dependent on the type of that lambda's explicit object parameter. + auto IsDependent = [&]() { + for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) { + auto *LSI = dyn_cast(Scope); + if (!LSI) + continue; - // Check whether a potential result satisfies the requirements of NOUR. - auto IsPotentialResultOdrUsed = [&](NamedDecl *D) { - // Any entity other than a VarDecl is always odr-used whenever it's named - // in a potentially-evaluated expression. - auto *VD = dyn_cast(D); - if (!VD) - return true; + if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) && + LSI->AfterParameterList) + return false; - // C++2a [basic.def.odr]p4: - // A variable x whose name appears as a potentially-evalauted expression - // e is odr-used by e unless - // -- x is a reference that is usable in constant expressions, or - // -- x is a variable of non-reference type that is usable in constant - // expressions and has no mutable subobjects, and e is an element of - // the set of potential results of an expression of - // non-volatile-qualified non-class type to which the lvalue-to-rvalue - // conversion is applied, or - // -- x is a variable of non-reference type, and e is an element of the - // set of potential results of a discarded-value expression to which - // the lvalue-to-rvalue conversion is not applied - // - // We check the first bullet and the "potentially-evaluated" condition in - // BuildDeclRefExpr. We check the type requirements in the second bullet - // in CheckLValueToRValueConversionOperand below. - switch (NOUR) { - case NOUR_None: - case NOUR_Unevaluated: - llvm_unreachable("unexpected non-odr-use-reason"); + const auto *MD = LSI->CallOperator; + if (MD->getType().isNull()) + continue; - case NOUR_Constant: - // Constant references were handled when they were built. - if (VD->getType()->isReferenceType()) - return true; - if (auto *RD = VD->getType()->getAsCXXRecordDecl()) - if (RD->hasMutableFields()) + const auto *Ty = MD->getType()->getAs(); + if (!Ty || !MD->isExplicitObjectMemberFunction() || + !Ty->getParamType(0)->isDependentType()) + continue; + + if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) { + if (C->isCopyCapture()) return true; - if (!VD->isUsableInConstantExpressions(S.Context)) - return true; - break; + continue; + } - case NOUR_Discarded: - if (VD->getType()->isReferenceType()) + if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval) return true; - break; } return false; - }; + }(); - // Mark that this expression does not constitute an odr-use. - auto MarkNotOdrUsed = [&] { - S.MaybeODRUseExprs.remove(E); - if (LambdaScopeInfo *LSI = S.getCurLambda()) - LSI->markVariableExprAsNonODRUsed(E); - }; + ID->setCapturedByCopyInLambdaWithExplicitObjectParameter( + IsDependent, SemaRef.getASTContext()); +} - // C++2a [basic.def.odr]p2: - // The set of potential results of an expression e is defined as follows: - switch (E->getStmtClass()) { - // -- If e is an id-expression, ... - case Expr::DeclRefExprClass: { - auto *DRE = cast(E); - if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl())) - break; +static void +MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E, + bool MightBeOdrUse, + llvm::DenseMap &RefsMinusAssignments) { + if (SemaRef.OpenMP().isInOpenMPDeclareTargetContext()) + SemaRef.OpenMP().checkDeclIsAllowedInOpenMPTarget(E, D); - // Rebuild as a non-odr-use DeclRefExpr. - MarkNotOdrUsed(); - return DeclRefExpr::Create( - S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(), - DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(), - DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(), - DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR); + if (VarDecl *Var = dyn_cast(D)) { + DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments); + if (SemaRef.getLangOpts().CPlusPlus) + FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, + Var, E); + return; + } + + if (BindingDecl *Decl = dyn_cast(D)) { + DoMarkBindingDeclReferenced(SemaRef, Loc, Decl, E); + if (SemaRef.getLangOpts().CPlusPlus) + FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, + Decl, E); + return; + } + SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse); + + // If this is a call to a method via a cast, also mark the method in the + // derived class used in case codegen can devirtualize the call. + const MemberExpr *ME = dyn_cast(E); + if (!ME) + return; + CXXMethodDecl *MD = dyn_cast(ME->getMemberDecl()); + if (!MD) + return; + // Only attempt to devirtualize if this is truly a virtual call. + bool IsVirtualCall = MD->isVirtual() && + ME->performsVirtualDispatch(SemaRef.getLangOpts()); + if (!IsVirtualCall) + return; + + // If it's possible to devirtualize the call, mark the called function + // referenced. + CXXMethodDecl *DM = MD->getDevirtualizedMethod( + ME->getBase(), SemaRef.getLangOpts().AppleKext); + if (DM) + SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); +} + +void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) { + // TODO: update this with DR# once a defect report is filed. + // C++11 defect. The address of a pure member should not be an ODR use, even + // if it's a qualified reference. + bool OdrUse = true; + if (const CXXMethodDecl *Method = dyn_cast(E->getDecl())) + if (Method->isVirtual() && + !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) + OdrUse = false; + + if (auto *FD = dyn_cast(E->getDecl())) { + if (!isUnevaluatedContext() && !isConstantEvaluatedContext() && + !isImmediateFunctionContext() && + !isCheckingDefaultArgumentOrInitializer() && + FD->isImmediateFunction() && !RebuildingImmediateInvocation && + !FD->isDependentContext()) + ExprEvalContexts.back().ReferenceToConsteval.insert(E); + } + MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, + RefsMinusAssignments); +} + +void Sema::MarkMemberReferenced(MemberExpr *E) { + // C++11 [basic.def.odr]p2: + // A non-overloaded function whose name appears as a potentially-evaluated + // expression or a member of a set of candidate functions, if selected by + // overload resolution when referred to from a potentially-evaluated + // expression, is odr-used, unless it is a pure virtual function and its + // name is not explicitly qualified. + bool MightBeOdrUse = true; + if (E->performsVirtualDispatch(getLangOpts())) { + if (CXXMethodDecl *Method = dyn_cast(E->getMemberDecl())) + if (Method->isPureVirtual()) + MightBeOdrUse = false; } + SourceLocation Loc = + E->getMemberLoc().isValid() ? E->getMemberLoc() : E->getBeginLoc(); + MarkExprReferenced(*this, Loc, E->getMemberDecl(), E, MightBeOdrUse, + RefsMinusAssignments); +} - case Expr::FunctionParmPackExprClass: { - auto *FPPE = cast(E); - // If any of the declarations in the pack is odr-used, then the expression - // as a whole constitutes an odr-use. - for (VarDecl *D : *FPPE) - if (IsPotentialResultOdrUsed(D)) - return ExprEmpty(); +void Sema::MarkFunctionParmPackReferenced(FunctionParmPackExpr *E) { + for (VarDecl *VD : *E) + MarkExprReferenced(*this, E->getParameterPackLocation(), VD, E, true, + RefsMinusAssignments); +} - // FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice, - // nothing cares about whether we marked this as an odr-use, but it might - // be useful for non-compiler tools. - MarkNotOdrUsed(); - break; +/// Perform marking for a reference to an arbitrary declaration. It +/// marks the declaration referenced, and performs odr-use checking for +/// functions and variables. This method should not be used when building a +/// normal expression which refers to a variable. +void Sema::MarkAnyDeclReferenced(SourceLocation Loc, Decl *D, + bool MightBeOdrUse) { + if (MightBeOdrUse) { + if (auto *VD = dyn_cast(D)) { + MarkVariableReferenced(Loc, VD); + return; + } } - - // -- If e is a subscripting operation with an array operand... - case Expr::ArraySubscriptExprClass: { - auto *ASE = cast(E); - Expr *OldBase = ASE->getBase()->IgnoreImplicit(); - if (!OldBase->getType()->isArrayType()) - break; - ExprResult Base = Rebuild(OldBase); - if (!Base.isUsable()) - return Base; - Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() : ASE->getLHS(); - Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() : ASE->getRHS(); - SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored. - return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS, - ASE->getRBracketLoc()); + if (auto *FD = dyn_cast(D)) { + MarkFunctionReferenced(Loc, FD, MightBeOdrUse); + return; } + D->setReferenced(); +} - case Expr::MemberExprClass: { - auto *ME = cast(E); - // -- If e is a class member access expression [...] naming a non-static - // data member... - if (isa(ME->getMemberDecl())) { - ExprResult Base = Rebuild(ME->getBase()); - if (!Base.isUsable()) - return Base; - return MemberExpr::Create( - S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(), - ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), - ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(), - CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(), - ME->getObjectKind(), ME->isNonOdrUse()); - } +namespace { + // Mark all of the declarations used by a type as referenced. + // FIXME: Not fully implemented yet! We need to have a better understanding + // of when we're entering a context we should not recurse into. + // FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to + // TreeTransforms rebuilding the type in a new context. Rather than + // duplicating the TreeTransform logic, we should consider reusing it here. + // Currently that causes problems when rebuilding LambdaExprs. + class MarkReferencedDecls : public RecursiveASTVisitor { + Sema &S; + SourceLocation Loc; - if (ME->getMemberDecl()->isCXXInstanceMember()) - break; + public: + typedef RecursiveASTVisitor Inherited; - // -- If e is a class member access expression naming a static data member, - // ... - if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl())) - break; + MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) { } - // Rebuild as a non-odr-use MemberExpr. - MarkNotOdrUsed(); - return MemberExpr::Create( - S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(), - ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(), - ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME), - ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR); - } + bool TraverseTemplateArgument(const TemplateArgument &Arg); + }; +} - case Expr::BinaryOperatorClass: { - auto *BO = cast(E); - Expr *LHS = BO->getLHS(); - Expr *RHS = BO->getRHS(); - // -- If e is a pointer-to-member expression of the form e1 .* e2 ... - if (BO->getOpcode() == BO_PtrMemD) { - ExprResult Sub = Rebuild(LHS); - if (!Sub.isUsable()) - return Sub; - BO->setLHS(Sub.get()); - // -- If e is a comma expression, ... - } else if (BO->getOpcode() == BO_Comma) { - ExprResult Sub = Rebuild(RHS); - if (!Sub.isUsable()) - return Sub; - BO->setRHS(Sub.get()); - } else { - break; +bool MarkReferencedDecls::TraverseTemplateArgument( + const TemplateArgument &Arg) { + { + // A non-type template argument is a constant-evaluated context. + EnterExpressionEvaluationContext Evaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + if (Arg.getKind() == TemplateArgument::Declaration) { + if (Decl *D = Arg.getAsDecl()) + S.MarkAnyDeclReferenced(Loc, D, true); + } else if (Arg.getKind() == TemplateArgument::Expression) { + S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false); } - return ExprResult(BO); - } - - // -- If e has the form (e1)... - case Expr::ParenExprClass: { - auto *PE = cast(E); - ExprResult Sub = Rebuild(PE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get()); } - // -- If e is a glvalue conditional expression, ... - // We don't apply this to a binary conditional operator. FIXME: Should we? - case Expr::ConditionalOperatorClass: { - auto *CO = cast(E); - ExprResult LHS = Rebuild(CO->getLHS()); - if (LHS.isInvalid()) - return ExprError(); - ExprResult RHS = Rebuild(CO->getRHS()); - if (RHS.isInvalid()) - return ExprError(); - if (!LHS.isUsable() && !RHS.isUsable()) - return ExprEmpty(); - if (!LHS.isUsable()) - LHS = CO->getLHS(); - if (!RHS.isUsable()) - RHS = CO->getRHS(); - return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(), - CO->getCond(), LHS.get(), RHS.get()); - } + return Inherited::TraverseTemplateArgument(Arg); +} - // [Clang extension] - // -- If e has the form __extension__ e1... - case Expr::UnaryOperatorClass: { - auto *UO = cast(E); - if (UO->getOpcode() != UO_Extension) - break; - ExprResult Sub = Rebuild(UO->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension, - Sub.get()); - } +void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) { + MarkReferencedDecls Marker(*this, Loc); + Marker.TraverseType(T); +} - // [Clang extension] - // -- If e has the form _Generic(...), the set of potential results is the - // union of the sets of potential results of the associated expressions. - case Expr::GenericSelectionExprClass: { - auto *GSE = cast(E); +namespace { +/// Helper class that marks all of the declarations referenced by +/// potentially-evaluated subexpressions as "referenced". +class EvaluatedExprMarker : public UsedDeclVisitor { +public: + typedef UsedDeclVisitor Inherited; + bool SkipLocalVariables; + ArrayRef StopAt; - SmallVector AssocExprs; - bool AnyChanged = false; - for (Expr *OrigAssocExpr : GSE->getAssocExprs()) { - ExprResult AssocExpr = Rebuild(OrigAssocExpr); - if (AssocExpr.isInvalid()) - return ExprError(); - if (AssocExpr.isUsable()) { - AssocExprs.push_back(AssocExpr.get()); - AnyChanged = true; - } else { - AssocExprs.push_back(OrigAssocExpr); - } - } + EvaluatedExprMarker(Sema &S, bool SkipLocalVariables, + ArrayRef StopAt) + : Inherited(S), SkipLocalVariables(SkipLocalVariables), StopAt(StopAt) {} - void *ExOrTy = nullptr; - bool IsExpr = GSE->isExprPredicate(); - if (IsExpr) - ExOrTy = GSE->getControllingExpr(); - else - ExOrTy = GSE->getControllingType(); - return AnyChanged ? S.CreateGenericSelectionExpr( - GSE->getGenericLoc(), GSE->getDefaultLoc(), - GSE->getRParenLoc(), IsExpr, ExOrTy, - GSE->getAssocTypeSourceInfos(), AssocExprs) - : ExprEmpty(); + void visitUsedDecl(SourceLocation Loc, Decl *D) { + S.MarkFunctionReferenced(Loc, cast(D)); } - // [Clang extension] - // -- If e has the form __builtin_choose_expr(...), the set of potential - // results is the union of the sets of potential results of the - // second and third subexpressions. - case Expr::ChooseExprClass: { - auto *CE = cast(E); - - ExprResult LHS = Rebuild(CE->getLHS()); - if (LHS.isInvalid()) - return ExprError(); + void Visit(Expr *E) { + if (llvm::is_contained(StopAt, E)) + return; + Inherited::Visit(E); + } - ExprResult RHS = Rebuild(CE->getLHS()); - if (RHS.isInvalid()) - return ExprError(); + void VisitConstantExpr(ConstantExpr *E) { + // Don't mark declarations within a ConstantExpression, as this expression + // will be evaluated and folded to a value. + } - if (!LHS.get() && !RHS.get()) - return ExprEmpty(); - if (!LHS.isUsable()) - LHS = CE->getLHS(); - if (!RHS.isUsable()) - RHS = CE->getRHS(); + void VisitDeclRefExpr(DeclRefExpr *E) { + // If we were asked not to visit local variables, don't. + if (SkipLocalVariables) { + if (VarDecl *VD = dyn_cast(E->getDecl())) + if (VD->hasLocalStorage()) + return; + } - return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(), - RHS.get(), CE->getRParenLoc()); + // FIXME: This can trigger the instantiation of the initializer of a + // variable, which can cause the expression to become value-dependent + // or error-dependent. Do we need to propagate the new dependence bits? + S.MarkDeclRefReferenced(E); } - // Step through non-syntactic nodes. - case Expr::ConstantExprClass: { - auto *CE = cast(E); - ExprResult Sub = Rebuild(CE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return ConstantExpr::Create(S.Context, Sub.get()); + void VisitMemberExpr(MemberExpr *E) { + S.MarkMemberReferenced(E); + Visit(E->getBase()); } +}; +} // namespace - // We could mostly rely on the recursive rebuilding to rebuild implicit - // casts, but not at the top level, so rebuild them here. - case Expr::ImplicitCastExprClass: { - auto *ICE = cast(E); - // Only step through the narrow set of cast kinds we expect to encounter. - // Anything else suggests we've left the region in which potential results - // can be found. - switch (ICE->getCastKind()) { - case CK_NoOp: - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: { - ExprResult Sub = Rebuild(ICE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - CXXCastPath Path(ICE->path()); - return S.ImpCastExprToType(Sub.get(), ICE->getType(), ICE->getCastKind(), - ICE->getValueKind(), &Path); - } +void Sema::MarkDeclarationsReferencedInExpr(Expr *E, + bool SkipLocalVariables, + ArrayRef StopAt) { + EvaluatedExprMarker(*this, SkipLocalVariables, StopAt).Visit(E); +} - default: - break; - } - break; +/// Emit a diagnostic when statements are reachable. +/// FIXME: check for reachability even in expressions for which we don't build a +/// CFG (eg, in the initializer of a global or in a constant expression). +/// For example, +/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } +bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD) { + if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { + if (!FunctionScopes.empty()) + FunctionScopes.back()->PossiblyUnreachableDiags.push_back( + sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; } - default: - break; + // The initializer of a constexpr variable or of the first declaration of a + // static data member is not syntactically a constant evaluated constant, + // but nonetheless is always required to be a constant expression, so we + // can skip diagnosing. + // FIXME: Using the mangling context here is a hack. + if (auto *VD = dyn_cast_or_null( + ExprEvalContexts.back().ManglingContextDecl)) { + if (VD->isConstexpr() || + (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) + return false; + // FIXME: For any other kind of variable, we should build a CFG for its + // initializer and check whether the context in question is reachable. } - // Can't traverse through this node. Nothing to do. - return ExprEmpty(); + Diag(Loc, PD); + return true; } -ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { - // Check whether the operand is or contains an object of non-trivial C union - // type. - if (E->getType().isVolatileQualified() && - (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || - E->getType().hasNonTrivialToPrimitiveCopyCUnion())) - checkNonTrivialCUnion(E->getType(), E->getExprLoc(), - Sema::NTCUC_LValueToRValueVolatile, - NTCUK_Destruct|NTCUK_Copy); +/// Emit a diagnostic that describes an effect on the run-time behavior +/// of the program being compiled. +/// +/// This routine emits the given diagnostic when the code currently being +/// type-checked is "potentially evaluated", meaning that there is a +/// possibility that the code will actually be executable. Code in sizeof() +/// expressions, code used only during overload resolution, etc., are not +/// potentially evaluated. This routine will suppress such diagnostics or, +/// in the absolutely nutty case of potentially potentially evaluated +/// expressions (C++ typeid), queue the diagnostic to potentially emit it +/// later. +/// +/// This routine should be used for all diagnostics that describe the run-time +/// behavior of a program, such as passing a non-POD value through an ellipsis. +/// Failure to do so will likely result in spurious diagnostics or failures +/// during overload resolution or within sizeof/alignof/typeof/typeid. +bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD) { - // C++2a [basic.def.odr]p4: - // [...] an expression of non-volatile-qualified non-class type to which - // the lvalue-to-rvalue conversion is applied [...] - if (E->getType().isVolatileQualified() || E->getType()->getAs()) - return E; + if (ExprEvalContexts.back().isDiscardedStatementContext()) + return false; - ExprResult Result = - rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant); - if (Result.isInvalid()) - return ExprError(); - return Result.get() ? Result : E; -} + switch (ExprEvalContexts.back().Context) { + case ExpressionEvaluationContext::Unevaluated: + case ExpressionEvaluationContext::UnevaluatedList: + case ExpressionEvaluationContext::UnevaluatedAbstract: + case ExpressionEvaluationContext::DiscardedStatement: + // The argument will never be evaluated, so don't complain. + break; -ExprResult Sema::ActOnConstantExpression(ExprResult Res) { - Res = CorrectDelayedTyposInExpr(Res); + case ExpressionEvaluationContext::ConstantEvaluated: + case ExpressionEvaluationContext::ImmediateFunctionContext: + // Relevant diagnostics should be produced by constant evaluation. + break; - if (!Res.isUsable()) - return Res; + case ExpressionEvaluationContext::PotentiallyEvaluated: + case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + return DiagIfReachable(Loc, Stmts, PD); + } - // If a constant-expression is a reference to a variable where we delay - // deciding whether it is an odr-use, just assume we will apply the - // lvalue-to-rvalue conversion. In the one case where this doesn't happen - // (a non-type template argument), we have special handling anyway. - return CheckLValueToRValueConversionOperand(Res.get()); + return false; } -void Sema::CleanupVarDeclMarking() { - // Iterate through a local copy in case MarkVarDeclODRUsed makes a recursive - // call. - MaybeODRUseExprSet LocalMaybeODRUseExprs; - std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs); +bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, + const PartialDiagnostic &PD) { + return DiagRuntimeBehavior( + Loc, Statement ? llvm::ArrayRef(Statement) : std::nullopt, PD); +} - for (Expr *E : LocalMaybeODRUseExprs) { - if (auto *DRE = dyn_cast(E)) { - MarkVarDeclODRUsed(cast(DRE->getDecl()), - DRE->getLocation(), *this); - } else if (auto *ME = dyn_cast(E)) { - MarkVarDeclODRUsed(cast(ME->getMemberDecl()), ME->getMemberLoc(), - *this); - } else if (auto *FP = dyn_cast(E)) { - for (VarDecl *VD : *FP) - MarkVarDeclODRUsed(VD, FP->getParameterPackLocation(), *this); - } else { - llvm_unreachable("Unexpected expression"); - } +bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, + CallExpr *CE, FunctionDecl *FD) { + if (ReturnType->isVoidType() || !ReturnType->isIncompleteType()) + return false; + + // If we're inside a decltype's expression, don't check for a valid return + // type or construct temporaries until we know whether this is the last call. + if (ExprEvalContexts.back().ExprContext == + ExpressionEvaluationContextRecord::EK_Decltype) { + ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE); + return false; } - assert(MaybeODRUseExprs.empty() && - "MarkVarDeclODRUsed failed to cleanup MaybeODRUseExprs?"); -} + class CallReturnIncompleteDiagnoser : public TypeDiagnoser { + FunctionDecl *FD; + CallExpr *CE; -static void DoMarkPotentialCapture(Sema &SemaRef, SourceLocation Loc, - ValueDecl *Var, Expr *E) { - VarDecl *VD = Var->getPotentiallyDecomposedVarDecl(); - if (!VD) - return; + public: + CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE) + : FD(FD), CE(CE) { } - const bool RefersToEnclosingScope = - (SemaRef.CurContext != VD->getDeclContext() && - VD->getDeclContext()->isFunctionOrMethod() && VD->hasLocalStorage()); - if (RefersToEnclosingScope) { - LambdaScopeInfo *const LSI = - SemaRef.getCurLambda(/*IgnoreNonLambdaCapturingScope=*/true); - if (LSI && (!LSI->CallOperator || - !LSI->CallOperator->Encloses(Var->getDeclContext()))) { - // If a variable could potentially be odr-used, defer marking it so - // until we finish analyzing the full expression for any - // lvalue-to-rvalue - // or discarded value conversions that would obviate odr-use. - // Add it to the list of potential captures that will be analyzed - // later (ActOnFinishFullExpr) for eventual capture and odr-use marking - // unless the variable is a reference that was initialized by a constant - // expression (this will never need to be captured or odr-used). - // - // FIXME: We can simplify this a lot after implementing P0588R1. - assert(E && "Capture variable should be used in an expression."); - if (!Var->getType()->isReferenceType() || - !VD->isUsableInConstantExpressions(SemaRef.Context)) - LSI->addPotentialCapture(E->IgnoreParens()); + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + if (!FD) { + S.Diag(Loc, diag::err_call_incomplete_return) + << T << CE->getSourceRange(); + return; + } + + S.Diag(Loc, diag::err_call_function_incomplete_return) + << CE->getSourceRange() << FD << T; + S.Diag(FD->getLocation(), diag::note_entity_declared_at) + << FD->getDeclName(); } - } + } Diagnoser(FD, CE); + + if (RequireCompleteType(Loc, ReturnType, Diagnoser)) + return true; + + return false; } -static void DoMarkVarDeclReferenced( - Sema &SemaRef, SourceLocation Loc, VarDecl *Var, Expr *E, - llvm::DenseMap &RefsMinusAssignments) { - assert((!E || isa(E) || isa(E) || - isa(E)) && - "Invalid Expr argument to DoMarkVarDeclReferenced"); - Var->setReferenced(); +// Diagnose the s/=/==/ and s/\|=/!=/ typos. Note that adding parentheses +// will prevent this condition from triggering, which is what we want. +void Sema::DiagnoseAssignmentAsCondition(Expr *E) { + SourceLocation Loc; + + unsigned diagnostic = diag::warn_condition_is_assignment; + bool IsOrAssign = false; + + if (BinaryOperator *Op = dyn_cast(E)) { + if (Op->getOpcode() != BO_Assign && Op->getOpcode() != BO_OrAssign) + return; + + IsOrAssign = Op->getOpcode() == BO_OrAssign; + + // Greylist some idioms by putting them into a warning subcategory. + if (ObjCMessageExpr *ME + = dyn_cast(Op->getRHS()->IgnoreParenCasts())) { + Selector Sel = ME->getSelector(); + + // self = [ init...] + if (ObjC().isSelfExpr(Op->getLHS()) && ME->getMethodFamily() == OMF_init) + diagnostic = diag::warn_condition_is_idiomatic_assignment; + + // = [ nextObject] + else if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "nextObject") + diagnostic = diag::warn_condition_is_idiomatic_assignment; + } - if (Var->isInvalidDecl()) + Loc = Op->getOperatorLoc(); + } else if (CXXOperatorCallExpr *Op = dyn_cast(E)) { + if (Op->getOperator() != OO_Equal && Op->getOperator() != OO_PipeEqual) + return; + + IsOrAssign = Op->getOperator() == OO_PipeEqual; + Loc = Op->getOperatorLoc(); + } else if (PseudoObjectExpr *POE = dyn_cast(E)) + return DiagnoseAssignmentAsCondition(POE->getSyntacticForm()); + else { + // Not an assignment. return; + } - auto *MSI = Var->getMemberSpecializationInfo(); - TemplateSpecializationKind TSK = MSI ? MSI->getTemplateSpecializationKind() - : Var->getTemplateSpecializationKind(); + Diag(Loc, diagnostic) << E->getSourceRange(); - OdrUseContext OdrUse = isOdrUseContext(SemaRef); - bool UsableInConstantExpr = - Var->mightBeUsableInConstantExpressions(SemaRef.Context); + SourceLocation Open = E->getBeginLoc(); + SourceLocation Close = getLocForEndOfToken(E->getSourceRange().getEnd()); + Diag(Loc, diag::note_condition_assign_silence) + << FixItHint::CreateInsertion(Open, "(") + << FixItHint::CreateInsertion(Close, ")"); - if (Var->isLocalVarDeclOrParm() && !Var->hasExternalStorage()) { - RefsMinusAssignments.insert({Var, 0}).first->getSecond()++; - } + if (IsOrAssign) + Diag(Loc, diag::note_condition_or_assign_to_comparison) + << FixItHint::CreateReplacement(Loc, "!="); + else + Diag(Loc, diag::note_condition_assign_to_comparison) + << FixItHint::CreateReplacement(Loc, "=="); +} - // C++20 [expr.const]p12: - // A variable [...] is needed for constant evaluation if it is [...] a - // variable whose name appears as a potentially constant evaluated - // expression that is either a contexpr variable or is of non-volatile - // const-qualified integral type or of reference type - bool NeededForConstantEvaluation = - isPotentiallyConstantEvaluatedContext(SemaRef) && UsableInConstantExpr; +void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) { + // Don't warn if the parens came from a macro. + SourceLocation parenLoc = ParenE->getBeginLoc(); + if (parenLoc.isInvalid() || parenLoc.isMacroID()) + return; + // Don't warn for dependent expressions. + if (ParenE->isTypeDependent()) + return; - bool NeedDefinition = - OdrUse == OdrUseContext::Used || NeededForConstantEvaluation; + Expr *E = ParenE->IgnoreParens(); - assert(!isa(Var) && - "Can't instantiate a partial template specialization."); + if (BinaryOperator *opE = dyn_cast(E)) + if (opE->getOpcode() == BO_EQ && + opE->getLHS()->IgnoreParenImpCasts()->isModifiableLvalue(Context) + == Expr::MLV_Valid) { + SourceLocation Loc = opE->getOperatorLoc(); - // If this might be a member specialization of a static data member, check - // the specialization is visible. We already did the checks for variable - // template specializations when we created them. - if (NeedDefinition && TSK != TSK_Undeclared && - !isa(Var)) - SemaRef.checkSpecializationVisibility(Loc, Var); + Diag(Loc, diag::warn_equality_with_extra_parens) << E->getSourceRange(); + SourceRange ParenERange = ParenE->getSourceRange(); + Diag(Loc, diag::note_equality_comparison_silence) + << FixItHint::CreateRemoval(ParenERange.getBegin()) + << FixItHint::CreateRemoval(ParenERange.getEnd()); + Diag(Loc, diag::note_equality_comparison_to_assign) + << FixItHint::CreateReplacement(Loc, "="); + } +} - // Perform implicit instantiation of static data members, static data member - // templates of class templates, and variable template specializations. Delay - // instantiations of variable templates, except for those that could be used - // in a constant expression. - if (NeedDefinition && isTemplateInstantiation(TSK)) { - // Per C++17 [temp.explicit]p10, we may instantiate despite an explicit - // instantiation declaration if a variable is usable in a constant - // expression (among other cases). - bool TryInstantiating = - TSK == TSK_ImplicitInstantiation || - (TSK == TSK_ExplicitInstantiationDeclaration && UsableInConstantExpr); +ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, + bool IsConstexpr) { + DiagnoseAssignmentAsCondition(E); + if (ParenExpr *parenE = dyn_cast(E)) + DiagnoseEqualityWithExtraParens(parenE); - if (TryInstantiating) { - SourceLocation PointOfInstantiation = - MSI ? MSI->getPointOfInstantiation() : Var->getPointOfInstantiation(); - bool FirstInstantiation = PointOfInstantiation.isInvalid(); - if (FirstInstantiation) { - PointOfInstantiation = Loc; - if (MSI) - MSI->setPointOfInstantiation(PointOfInstantiation); - // FIXME: Notify listener. - else - Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); - } + ExprResult result = CheckPlaceholderExpr(E); + if (result.isInvalid()) return ExprError(); + E = result.get(); - if (UsableInConstantExpr) { - // Do not defer instantiations of variables that could be used in a - // constant expression. - SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] { - SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); - }); + if (!E->isTypeDependent()) { + if (getLangOpts().CPlusPlus) + return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4 - // Re-set the member to trigger a recomputation of the dependence bits - // for the expression. - if (auto *DRE = dyn_cast_or_null(E)) - DRE->setDecl(DRE->getDecl()); - else if (auto *ME = dyn_cast_or_null(E)) - ME->setMemberDecl(ME->getMemberDecl()); - } else if (FirstInstantiation) { - SemaRef.PendingInstantiations - .push_back(std::make_pair(Var, PointOfInstantiation)); - } else { - bool Inserted = false; - for (auto &I : SemaRef.SavedPendingInstantiations) { - auto Iter = llvm::find_if( - I, [Var](const Sema::PendingImplicitInstantiation &P) { - return P.first == Var; - }); - if (Iter != I.end()) { - SemaRef.PendingInstantiations.push_back(*Iter); - I.erase(Iter); - Inserted = true; - break; - } - } + ExprResult ERes = DefaultFunctionArrayLvalueConversion(E); + if (ERes.isInvalid()) + return ExprError(); + E = ERes.get(); - // FIXME: For a specialization of a variable template, we don't - // distinguish between "declaration and type implicitly instantiated" - // and "implicit instantiation of definition requested", so we have - // no direct way to avoid enqueueing the pending instantiation - // multiple times. - if (isa(Var) && !Inserted) - SemaRef.PendingInstantiations - .push_back(std::make_pair(Var, PointOfInstantiation)); - } + QualType T = E->getType(); + if (!T->isScalarType()) { // C99 6.8.4.1p1 + Diag(Loc, diag::err_typecheck_statement_requires_scalar) + << T << E->getSourceRange(); + return ExprError(); } + CheckBoolLikeConversion(E, Loc); } - // C++2a [basic.def.odr]p4: - // A variable x whose name appears as a potentially-evaluated expression e - // is odr-used by e unless - // -- x is a reference that is usable in constant expressions - // -- x is a variable of non-reference type that is usable in constant - // expressions and has no mutable subobjects [FIXME], and e is an - // element of the set of potential results of an expression of - // non-volatile-qualified non-class type to which the lvalue-to-rvalue - // conversion is applied - // -- x is a variable of non-reference type, and e is an element of the set - // of potential results of a discarded-value expression to which the - // lvalue-to-rvalue conversion is not applied [FIXME] - // - // We check the first part of the second bullet here, and - // Sema::CheckLValueToRValueConversionOperand deals with the second part. - // FIXME: To get the third bullet right, we need to delay this even for - // variables that are not usable in constant expressions. - - // If we already know this isn't an odr-use, there's nothing more to do. - if (DeclRefExpr *DRE = dyn_cast_or_null(E)) - if (DRE->isNonOdrUse()) - return; - if (MemberExpr *ME = dyn_cast_or_null(E)) - if (ME->isNonOdrUse()) - return; + return E; +} - switch (OdrUse) { - case OdrUseContext::None: - // In some cases, a variable may not have been marked unevaluated, if it - // appears in a defaukt initializer. - assert((!E || isa(E) || - SemaRef.isUnevaluatedContext()) && - "missing non-odr-use marking for unevaluated decl ref"); - break; +Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, + Expr *SubExpr, ConditionKind CK, + bool MissingOK) { + // MissingOK indicates whether having no condition expression is valid + // (for loop) or invalid (e.g. while loop). + if (!SubExpr) + return MissingOK ? ConditionResult() : ConditionError(); - case OdrUseContext::FormallyOdrUsed: - // FIXME: Ignoring formal odr-uses results in incorrect lambda capture - // behavior. + ExprResult Cond; + switch (CK) { + case ConditionKind::Boolean: + Cond = CheckBooleanCondition(Loc, SubExpr); break; - case OdrUseContext::Used: - // If we might later find that this expression isn't actually an odr-use, - // delay the marking. - if (E && Var->isUsableInConstantExpressions(SemaRef.Context)) - SemaRef.MaybeODRUseExprs.insert(E); - else - MarkVarDeclODRUsed(Var, Loc, SemaRef); + case ConditionKind::ConstexprIf: + Cond = CheckBooleanCondition(Loc, SubExpr, true); break; - case OdrUseContext::Dependent: - // If this is a dependent context, we don't need to mark variables as - // odr-used, but we may still need to track them for lambda capture. - // FIXME: Do we also need to do this inside dependent typeid expressions - // (which are modeled as unevaluated at this point)? - DoMarkPotentialCapture(SemaRef, Loc, Var, E); + case ConditionKind::Switch: + Cond = CheckSwitchCondition(Loc, SubExpr); break; } -} - -static void DoMarkBindingDeclReferenced(Sema &SemaRef, SourceLocation Loc, - BindingDecl *BD, Expr *E) { - BD->setReferenced(); - - if (BD->isInvalidDecl()) - return; - - OdrUseContext OdrUse = isOdrUseContext(SemaRef); - if (OdrUse == OdrUseContext::Used) { - QualType CaptureType, DeclRefType; - SemaRef.tryCaptureVariable(BD, Loc, Sema::TryCapture_Implicit, - /*EllipsisLoc*/ SourceLocation(), - /*BuildAndDiagnose*/ true, CaptureType, - DeclRefType, - /*FunctionScopeIndexToStopAt*/ nullptr); - } else if (OdrUse == OdrUseContext::Dependent) { - DoMarkPotentialCapture(SemaRef, Loc, BD, E); + if (Cond.isInvalid()) { + Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), + {SubExpr}, PreferredConditionType(CK)); + if (!Cond.get()) + return ConditionError(); } -} + // FIXME: FullExprArg doesn't have an invalid bit, so check nullness instead. + FullExprArg FullExpr = MakeFullExpr(Cond.get(), Loc); + if (!FullExpr.get()) + return ConditionError(); -void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { - DoMarkVarDeclReferenced(*this, Loc, Var, nullptr, RefsMinusAssignments); + return ConditionResult(*this, nullptr, FullExpr, + CK == ConditionKind::ConstexprIf); } -// C++ [temp.dep.expr]p3: -// An id-expression is type-dependent if it contains: -// - an identifier associated by name lookup with an entity captured by copy -// in a lambda-expression that has an explicit object parameter whose type -// is dependent ([dcl.fct]), -static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter( - Sema &SemaRef, ValueDecl *D, Expr *E) { - auto *ID = dyn_cast(E); - if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture()) - return; +namespace { + /// A visitor for rebuilding a call to an __unknown_any expression + /// to have an appropriate type. + struct RebuildUnknownAnyFunction + : StmtVisitor { - // If any enclosing lambda with a dependent explicit object parameter either - // explicitly captures the variable by value, or has a capture default of '=' - // and does not capture the variable by reference, then the type of the DRE - // is dependent on the type of that lambda's explicit object parameter. - auto IsDependent = [&]() { - for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) { - auto *LSI = dyn_cast(Scope); - if (!LSI) - continue; + Sema &S; - if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) && - LSI->AfterParameterList) - return false; + RebuildUnknownAnyFunction(Sema &S) : S(S) {} - const auto *MD = LSI->CallOperator; - if (MD->getType().isNull()) - continue; + ExprResult VisitStmt(Stmt *S) { + llvm_unreachable("unexpected statement!"); + } - const auto *Ty = MD->getType()->getAs(); - if (!Ty || !MD->isExplicitObjectMemberFunction() || - !Ty->getParamType(0)->isDependentType()) - continue; + ExprResult VisitExpr(Expr *E) { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_call) + << E->getSourceRange(); + return ExprError(); + } - if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) { - if (C->isCopyCapture()) - return true; - continue; - } + /// Rebuild an expression which simply semantically wraps another + /// expression which it shares the type and value kind of. + template ExprResult rebuildSugarExpr(T *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); - if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval) - return true; + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(SubExpr->getType()); + E->setValueKind(SubExpr->getValueKind()); + assert(E->getObjectKind() == OK_Ordinary); + return E; } - return false; - }(); - ID->setCapturedByCopyInLambdaWithExplicitObjectParameter( - IsDependent, SemaRef.getASTContext()); -} + ExprResult VisitParenExpr(ParenExpr *E) { + return rebuildSugarExpr(E); + } -static void -MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E, - bool MightBeOdrUse, - llvm::DenseMap &RefsMinusAssignments) { - if (SemaRef.OpenMP().isInOpenMPDeclareTargetContext()) - SemaRef.OpenMP().checkDeclIsAllowedInOpenMPTarget(E, D); + ExprResult VisitUnaryExtension(UnaryOperator *E) { + return rebuildSugarExpr(E); + } - if (VarDecl *Var = dyn_cast(D)) { - DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments); - if (SemaRef.getLangOpts().CPlusPlus) - FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, - Var, E); - return; - } + ExprResult VisitUnaryAddrOf(UnaryOperator *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); - if (BindingDecl *Decl = dyn_cast(D)) { - DoMarkBindingDeclReferenced(SemaRef, Loc, Decl, E); - if (SemaRef.getLangOpts().CPlusPlus) - FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, - Decl, E); - return; - } - SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse); + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(S.Context.getPointerType(SubExpr->getType())); + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); + return E; + } - // If this is a call to a method via a cast, also mark the method in the - // derived class used in case codegen can devirtualize the call. - const MemberExpr *ME = dyn_cast(E); - if (!ME) - return; - CXXMethodDecl *MD = dyn_cast(ME->getMemberDecl()); - if (!MD) - return; - // Only attempt to devirtualize if this is truly a virtual call. - bool IsVirtualCall = MD->isVirtual() && - ME->performsVirtualDispatch(SemaRef.getLangOpts()); - if (!IsVirtualCall) - return; + ExprResult resolveDecl(Expr *E, ValueDecl *VD) { + if (!isa(VD)) return VisitExpr(E); - // If it's possible to devirtualize the call, mark the called function - // referenced. - CXXMethodDecl *DM = MD->getDevirtualizedMethod( - ME->getBase(), SemaRef.getLangOpts().AppleKext); - if (DM) - SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); -} + E->setType(VD->getType()); -void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) { - // TODO: update this with DR# once a defect report is filed. - // C++11 defect. The address of a pure member should not be an ODR use, even - // if it's a qualified reference. - bool OdrUse = true; - if (const CXXMethodDecl *Method = dyn_cast(E->getDecl())) - if (Method->isVirtual() && - !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) - OdrUse = false; + assert(E->isPRValue()); + if (S.getLangOpts().CPlusPlus && + !(isa(VD) && + cast(VD)->isInstance())) + E->setValueKind(VK_LValue); - if (auto *FD = dyn_cast(E->getDecl())) { - if (!isUnevaluatedContext() && !isConstantEvaluatedContext() && - !isImmediateFunctionContext() && - !isCheckingDefaultArgumentOrInitializer() && - FD->isImmediateFunction() && !RebuildingImmediateInvocation && - !FD->isDependentContext()) - ExprEvalContexts.back().ReferenceToConsteval.insert(E); - } - MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, - RefsMinusAssignments); -} + return E; + } -void Sema::MarkMemberReferenced(MemberExpr *E) { - // C++11 [basic.def.odr]p2: - // A non-overloaded function whose name appears as a potentially-evaluated - // expression or a member of a set of candidate functions, if selected by - // overload resolution when referred to from a potentially-evaluated - // expression, is odr-used, unless it is a pure virtual function and its - // name is not explicitly qualified. - bool MightBeOdrUse = true; - if (E->performsVirtualDispatch(getLangOpts())) { - if (CXXMethodDecl *Method = dyn_cast(E->getMemberDecl())) - if (Method->isPureVirtual()) - MightBeOdrUse = false; - } - SourceLocation Loc = - E->getMemberLoc().isValid() ? E->getMemberLoc() : E->getBeginLoc(); - MarkExprReferenced(*this, Loc, E->getMemberDecl(), E, MightBeOdrUse, - RefsMinusAssignments); -} + ExprResult VisitMemberExpr(MemberExpr *E) { + return resolveDecl(E, E->getMemberDecl()); + } -void Sema::MarkFunctionParmPackReferenced(FunctionParmPackExpr *E) { - for (VarDecl *VD : *E) - MarkExprReferenced(*this, E->getParameterPackLocation(), VD, E, true, - RefsMinusAssignments); + ExprResult VisitDeclRefExpr(DeclRefExpr *E) { + return resolveDecl(E, E->getDecl()); + } + }; } -/// Perform marking for a reference to an arbitrary declaration. It -/// marks the declaration referenced, and performs odr-use checking for -/// functions and variables. This method should not be used when building a -/// normal expression which refers to a variable. -void Sema::MarkAnyDeclReferenced(SourceLocation Loc, Decl *D, - bool MightBeOdrUse) { - if (MightBeOdrUse) { - if (auto *VD = dyn_cast(D)) { - MarkVariableReferenced(Loc, VD); - return; - } - } - if (auto *FD = dyn_cast(D)) { - MarkFunctionReferenced(Loc, FD, MightBeOdrUse); - return; - } - D->setReferenced(); +/// Given a function expression of unknown-any type, try to rebuild it +/// to have a function type. +static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *FunctionExpr) { + ExprResult Result = RebuildUnknownAnyFunction(S).Visit(FunctionExpr); + if (Result.isInvalid()) return ExprError(); + return S.DefaultFunctionArrayConversion(Result.get()); } namespace { - // Mark all of the declarations used by a type as referenced. - // FIXME: Not fully implemented yet! We need to have a better understanding - // of when we're entering a context we should not recurse into. - // FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to - // TreeTransforms rebuilding the type in a new context. Rather than - // duplicating the TreeTransform logic, we should consider reusing it here. - // Currently that causes problems when rebuilding LambdaExprs. - class MarkReferencedDecls : public RecursiveASTVisitor { + /// A visitor for rebuilding an expression of type __unknown_anytype + /// into one which resolves the type directly on the referring + /// expression. Strict preservation of the original source + /// structure is not a goal. + struct RebuildUnknownAnyExpr + : StmtVisitor { + Sema &S; - SourceLocation Loc; - public: - typedef RecursiveASTVisitor Inherited; + /// The current destination type. + QualType DestType; - MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) { } + RebuildUnknownAnyExpr(Sema &S, QualType CastType) + : S(S), DestType(CastType) {} + + ExprResult VisitStmt(Stmt *S) { + llvm_unreachable("unexpected statement!"); + } + + ExprResult VisitExpr(Expr *E) { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) + << E->getSourceRange(); + return ExprError(); + } + + ExprResult VisitCallExpr(CallExpr *E); + ExprResult VisitObjCMessageExpr(ObjCMessageExpr *E); + + /// Rebuild an expression which simply semantically wraps another + /// expression which it shares the type and value kind of. + template ExprResult rebuildSugarExpr(T *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(SubExpr->getType()); + E->setValueKind(SubExpr->getValueKind()); + assert(E->getObjectKind() == OK_Ordinary); + return E; + } - bool TraverseTemplateArgument(const TemplateArgument &Arg); - }; -} + ExprResult VisitParenExpr(ParenExpr *E) { + return rebuildSugarExpr(E); + } -bool MarkReferencedDecls::TraverseTemplateArgument( - const TemplateArgument &Arg) { - { - // A non-type template argument is a constant-evaluated context. - EnterExpressionEvaluationContext Evaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated); - if (Arg.getKind() == TemplateArgument::Declaration) { - if (Decl *D = Arg.getAsDecl()) - S.MarkAnyDeclReferenced(Loc, D, true); - } else if (Arg.getKind() == TemplateArgument::Expression) { - S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false); + ExprResult VisitUnaryExtension(UnaryOperator *E) { + return rebuildSugarExpr(E); } - } - return Inherited::TraverseTemplateArgument(Arg); -} + ExprResult VisitUnaryAddrOf(UnaryOperator *E) { + const PointerType *Ptr = DestType->getAs(); + if (!Ptr) { + S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof) + << E->getSourceRange(); + return ExprError(); + } -void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) { - MarkReferencedDecls Marker(*this, Loc); - Marker.TraverseType(T); -} + if (isa(E->getSubExpr())) { + S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof_call) + << E->getSourceRange(); + return ExprError(); + } -namespace { -/// Helper class that marks all of the declarations referenced by -/// potentially-evaluated subexpressions as "referenced". -class EvaluatedExprMarker : public UsedDeclVisitor { -public: - typedef UsedDeclVisitor Inherited; - bool SkipLocalVariables; - ArrayRef StopAt; + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); + E->setType(DestType); - EvaluatedExprMarker(Sema &S, bool SkipLocalVariables, - ArrayRef StopAt) - : Inherited(S), SkipLocalVariables(SkipLocalVariables), StopAt(StopAt) {} + // Build the sub-expression as if it were an object of the pointee type. + DestType = Ptr->getPointeeType(); + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); + E->setSubExpr(SubResult.get()); + return E; + } - void visitUsedDecl(SourceLocation Loc, Decl *D) { - S.MarkFunctionReferenced(Loc, cast(D)); - } + ExprResult VisitImplicitCastExpr(ImplicitCastExpr *E); - void Visit(Expr *E) { - if (llvm::is_contained(StopAt, E)) - return; - Inherited::Visit(E); - } + ExprResult resolveDecl(Expr *E, ValueDecl *VD); - void VisitConstantExpr(ConstantExpr *E) { - // Don't mark declarations within a ConstantExpression, as this expression - // will be evaluated and folded to a value. - } + ExprResult VisitMemberExpr(MemberExpr *E) { + return resolveDecl(E, E->getMemberDecl()); + } - void VisitDeclRefExpr(DeclRefExpr *E) { - // If we were asked not to visit local variables, don't. - if (SkipLocalVariables) { - if (VarDecl *VD = dyn_cast(E->getDecl())) - if (VD->hasLocalStorage()) - return; + ExprResult VisitDeclRefExpr(DeclRefExpr *E) { + return resolveDecl(E, E->getDecl()); } + }; +} - // FIXME: This can trigger the instantiation of the initializer of a - // variable, which can cause the expression to become value-dependent - // or error-dependent. Do we need to propagate the new dependence bits? - S.MarkDeclRefReferenced(E); - } +/// Rebuilds a call expression which yielded __unknown_anytype. +ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) { + Expr *CalleeExpr = E->getCallee(); - void VisitMemberExpr(MemberExpr *E) { - S.MarkMemberReferenced(E); - Visit(E->getBase()); + enum FnKind { + FK_MemberFunction, + FK_FunctionPointer, + FK_BlockPointer + }; + + FnKind Kind; + QualType CalleeType = CalleeExpr->getType(); + if (CalleeType == S.Context.BoundMemberTy) { + assert(isa(E) || isa(E)); + Kind = FK_MemberFunction; + CalleeType = Expr::findBoundMemberType(CalleeExpr); + } else if (const PointerType *Ptr = CalleeType->getAs()) { + CalleeType = Ptr->getPointeeType(); + Kind = FK_FunctionPointer; + } else { + CalleeType = CalleeType->castAs()->getPointeeType(); + Kind = FK_BlockPointer; } -}; -} // namespace + const FunctionType *FnType = CalleeType->castAs(); -void Sema::MarkDeclarationsReferencedInExpr(Expr *E, - bool SkipLocalVariables, - ArrayRef StopAt) { - EvaluatedExprMarker(*this, SkipLocalVariables, StopAt).Visit(E); -} + // Verify that this is a legal result type of a function. + if (DestType->isArrayType() || DestType->isFunctionType()) { + unsigned diagID = diag::err_func_returning_array_function; + if (Kind == FK_BlockPointer) + diagID = diag::err_block_returning_array_function; -/// Emit a diagnostic when statements are reachable. -/// FIXME: check for reachability even in expressions for which we don't build a -/// CFG (eg, in the initializer of a global or in a constant expression). -/// For example, -/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } -bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, - const PartialDiagnostic &PD) { - if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { - if (!FunctionScopes.empty()) - FunctionScopes.back()->PossiblyUnreachableDiags.push_back( - sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); - return true; + S.Diag(E->getExprLoc(), diagID) + << DestType->isFunctionType() << DestType; + return ExprError(); } - // The initializer of a constexpr variable or of the first declaration of a - // static data member is not syntactically a constant evaluated constant, - // but nonetheless is always required to be a constant expression, so we - // can skip diagnosing. - // FIXME: Using the mangling context here is a hack. - if (auto *VD = dyn_cast_or_null( - ExprEvalContexts.back().ManglingContextDecl)) { - if (VD->isConstexpr() || - (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) - return false; - // FIXME: For any other kind of variable, we should build a CFG for its - // initializer and check whether the context in question is reachable. - } + // Otherwise, go ahead and set DestType as the call's result. + E->setType(DestType.getNonLValueExprType(S.Context)); + E->setValueKind(Expr::getValueKindForType(DestType)); + assert(E->getObjectKind() == OK_Ordinary); - Diag(Loc, PD); - return true; -} + // Rebuild the function type, replacing the result type with DestType. + const FunctionProtoType *Proto = dyn_cast(FnType); + if (Proto) { + // __unknown_anytype(...) is a special case used by the debugger when + // it has no idea what a function's signature is. + // + // We want to build this call essentially under the K&R + // unprototyped rules, but making a FunctionNoProtoType in C++ + // would foul up all sorts of assumptions. However, we cannot + // simply pass all arguments as variadic arguments, nor can we + // portably just call the function under a non-variadic type; see + // the comment on IR-gen's TargetInfo::isNoProtoCallVariadic. + // However, it turns out that in practice it is generally safe to + // call a function declared as "A foo(B,C,D);" under the prototype + // "A foo(B,C,D,...);". The only known exception is with the + // Windows ABI, where any variadic function is implicitly cdecl + // regardless of its normal CC. Therefore we change the parameter + // types to match the types of the arguments. + // + // This is a hack, but it is far superior to moving the + // corresponding target-specific code from IR-gen to Sema/AST. -/// Emit a diagnostic that describes an effect on the run-time behavior -/// of the program being compiled. -/// -/// This routine emits the given diagnostic when the code currently being -/// type-checked is "potentially evaluated", meaning that there is a -/// possibility that the code will actually be executable. Code in sizeof() -/// expressions, code used only during overload resolution, etc., are not -/// potentially evaluated. This routine will suppress such diagnostics or, -/// in the absolutely nutty case of potentially potentially evaluated -/// expressions (C++ typeid), queue the diagnostic to potentially emit it -/// later. -/// -/// This routine should be used for all diagnostics that describe the run-time -/// behavior of a program, such as passing a non-POD value through an ellipsis. -/// Failure to do so will likely result in spurious diagnostics or failures -/// during overload resolution or within sizeof/alignof/typeof/typeid. -bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef Stmts, - const PartialDiagnostic &PD) { + ArrayRef ParamTypes = Proto->getParamTypes(); + SmallVector ArgTypes; + if (ParamTypes.empty() && Proto->isVariadic()) { // the special case + ArgTypes.reserve(E->getNumArgs()); + for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { + ArgTypes.push_back(S.Context.getReferenceQualifiedType(E->getArg(i))); + } + ParamTypes = ArgTypes; + } + DestType = S.Context.getFunctionType(DestType, ParamTypes, + Proto->getExtProtoInfo()); + } else { + DestType = S.Context.getFunctionNoProtoType(DestType, + FnType->getExtInfo()); + } - if (ExprEvalContexts.back().isDiscardedStatementContext()) - return false; + // Rebuild the appropriate pointer-to-function type. + switch (Kind) { + case FK_MemberFunction: + // Nothing to do. + break; - switch (ExprEvalContexts.back().Context) { - case ExpressionEvaluationContext::Unevaluated: - case ExpressionEvaluationContext::UnevaluatedList: - case ExpressionEvaluationContext::UnevaluatedAbstract: - case ExpressionEvaluationContext::DiscardedStatement: - // The argument will never be evaluated, so don't complain. + case FK_FunctionPointer: + DestType = S.Context.getPointerType(DestType); break; - case ExpressionEvaluationContext::ConstantEvaluated: - case ExpressionEvaluationContext::ImmediateFunctionContext: - // Relevant diagnostics should be produced by constant evaluation. + case FK_BlockPointer: + DestType = S.Context.getBlockPointerType(DestType); break; + } + + // Finally, we can recurse. + ExprResult CalleeResult = Visit(CalleeExpr); + if (!CalleeResult.isUsable()) return ExprError(); + E->setCallee(CalleeResult.get()); + + // Bind a temporary if necessary. + return S.MaybeBindToTemporary(E); +} + +ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *E) { + // Verify that this is a legal result type of a call. + if (DestType->isArrayType() || DestType->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_func_returning_array_function) + << DestType->isFunctionType() << DestType; + return ExprError(); + } - case ExpressionEvaluationContext::PotentiallyEvaluated: - case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - return DiagIfReachable(Loc, Stmts, PD); + // Rewrite the method result type if available. + if (ObjCMethodDecl *Method = E->getMethodDecl()) { + assert(Method->getReturnType() == S.Context.UnknownAnyTy); + Method->setReturnType(DestType); } - return false; -} + // Change the type of the message. + E->setType(DestType.getNonReferenceType()); + E->setValueKind(Expr::getValueKindForType(DestType)); -bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, - const PartialDiagnostic &PD) { - return DiagRuntimeBehavior( - Loc, Statement ? llvm::ArrayRef(Statement) : std::nullopt, PD); + return S.MaybeBindToTemporary(E); } -bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, - CallExpr *CE, FunctionDecl *FD) { - if (ReturnType->isVoidType() || !ReturnType->isIncompleteType()) - return false; +ExprResult RebuildUnknownAnyExpr::VisitImplicitCastExpr(ImplicitCastExpr *E) { + // The only case we should ever see here is a function-to-pointer decay. + if (E->getCastKind() == CK_FunctionToPointerDecay) { + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); - // If we're inside a decltype's expression, don't check for a valid return - // type or construct temporaries until we know whether this is the last call. - if (ExprEvalContexts.back().ExprContext == - ExpressionEvaluationContextRecord::EK_Decltype) { - ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE); - return false; - } + E->setType(DestType); - class CallReturnIncompleteDiagnoser : public TypeDiagnoser { - FunctionDecl *FD; - CallExpr *CE; + // Rebuild the sub-expression as the pointee (function) type. + DestType = DestType->castAs()->getPointeeType(); - public: - CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE) - : FD(FD), CE(CE) { } + ExprResult Result = Visit(E->getSubExpr()); + if (!Result.isUsable()) return ExprError(); - void diagnose(Sema &S, SourceLocation Loc, QualType T) override { - if (!FD) { - S.Diag(Loc, diag::err_call_incomplete_return) - << T << CE->getSourceRange(); - return; - } + E->setSubExpr(Result.get()); + return E; + } else if (E->getCastKind() == CK_LValueToRValue) { + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); - S.Diag(Loc, diag::err_call_function_incomplete_return) - << CE->getSourceRange() << FD << T; - S.Diag(FD->getLocation(), diag::note_entity_declared_at) - << FD->getDeclName(); - } - } Diagnoser(FD, CE); + assert(isa(E->getType())); - if (RequireCompleteType(Loc, ReturnType, Diagnoser)) - return true; + E->setType(DestType); - return false; -} + // The sub-expression has to be a lvalue reference, so rebuild it as such. + DestType = S.Context.getLValueReferenceType(DestType); -// Diagnose the s/=/==/ and s/\|=/!=/ typos. Note that adding parentheses -// will prevent this condition from triggering, which is what we want. -void Sema::DiagnoseAssignmentAsCondition(Expr *E) { - SourceLocation Loc; + ExprResult Result = Visit(E->getSubExpr()); + if (!Result.isUsable()) return ExprError(); - unsigned diagnostic = diag::warn_condition_is_assignment; - bool IsOrAssign = false; + E->setSubExpr(Result.get()); + return E; + } else { + llvm_unreachable("Unhandled cast type!"); + } +} - if (BinaryOperator *Op = dyn_cast(E)) { - if (Op->getOpcode() != BO_Assign && Op->getOpcode() != BO_OrAssign) - return; +ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) { + ExprValueKind ValueKind = VK_LValue; + QualType Type = DestType; - IsOrAssign = Op->getOpcode() == BO_OrAssign; + // We know how to make this work for certain kinds of decls: - // Greylist some idioms by putting them into a warning subcategory. - if (ObjCMessageExpr *ME - = dyn_cast(Op->getRHS()->IgnoreParenCasts())) { - Selector Sel = ME->getSelector(); + // - functions + if (FunctionDecl *FD = dyn_cast(VD)) { + if (const PointerType *Ptr = Type->getAs()) { + DestType = Ptr->getPointeeType(); + ExprResult Result = resolveDecl(E, VD); + if (Result.isInvalid()) return ExprError(); + return S.ImpCastExprToType(Result.get(), Type, CK_FunctionToPointerDecay, + VK_PRValue); + } - // self = [ init...] - if (ObjC().isSelfExpr(Op->getLHS()) && ME->getMethodFamily() == OMF_init) - diagnostic = diag::warn_condition_is_idiomatic_assignment; + if (!Type->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_unknown_any_function) + << VD << E->getSourceRange(); + return ExprError(); + } + if (const FunctionProtoType *FT = Type->getAs()) { + // We must match the FunctionDecl's type to the hack introduced in + // RebuildUnknownAnyExpr::VisitCallExpr to vararg functions of unknown + // type. See the lengthy commentary in that routine. + QualType FDT = FD->getType(); + const FunctionType *FnType = FDT->castAs(); + const FunctionProtoType *Proto = dyn_cast_or_null(FnType); + DeclRefExpr *DRE = dyn_cast(E); + if (DRE && Proto && Proto->getParamTypes().empty() && Proto->isVariadic()) { + SourceLocation Loc = FD->getLocation(); + FunctionDecl *NewFD = FunctionDecl::Create( + S.Context, FD->getDeclContext(), Loc, Loc, + FD->getNameInfo().getName(), DestType, FD->getTypeSourceInfo(), + SC_None, S.getCurFPFeatures().isFPConstrained(), + false /*isInlineSpecified*/, FD->hasPrototype(), + /*ConstexprKind*/ ConstexprSpecKind::Unspecified); - // = [ nextObject] - else if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "nextObject") - diagnostic = diag::warn_condition_is_idiomatic_assignment; + if (FD->getQualifier()) + NewFD->setQualifierInfo(FD->getQualifierLoc()); + + SmallVector Params; + for (const auto &AI : FT->param_types()) { + ParmVarDecl *Param = + S.BuildParmVarDeclForTypedef(FD, Loc, AI); + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } + NewFD->setParams(Params); + DRE->setDecl(NewFD); + VD = DRE->getDecl(); + } } - Loc = Op->getOperatorLoc(); - } else if (CXXOperatorCallExpr *Op = dyn_cast(E)) { - if (Op->getOperator() != OO_Equal && Op->getOperator() != OO_PipeEqual) - return; + if (CXXMethodDecl *MD = dyn_cast(FD)) + if (MD->isInstance()) { + ValueKind = VK_PRValue; + Type = S.Context.BoundMemberTy; + } - IsOrAssign = Op->getOperator() == OO_PipeEqual; - Loc = Op->getOperatorLoc(); - } else if (PseudoObjectExpr *POE = dyn_cast(E)) - return DiagnoseAssignmentAsCondition(POE->getSyntacticForm()); - else { - // Not an assignment. - return; - } + // Function references aren't l-values in C. + if (!S.getLangOpts().CPlusPlus) + ValueKind = VK_PRValue; - Diag(Loc, diagnostic) << E->getSourceRange(); + // - variables + } else if (isa(VD)) { + if (const ReferenceType *RefTy = Type->getAs()) { + Type = RefTy->getPointeeType(); + } else if (Type->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_unknown_any_var_function_type) + << VD << E->getSourceRange(); + return ExprError(); + } - SourceLocation Open = E->getBeginLoc(); - SourceLocation Close = getLocForEndOfToken(E->getSourceRange().getEnd()); - Diag(Loc, diag::note_condition_assign_silence) - << FixItHint::CreateInsertion(Open, "(") - << FixItHint::CreateInsertion(Close, ")"); + // - nothing else + } else { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_decl) + << VD << E->getSourceRange(); + return ExprError(); + } - if (IsOrAssign) - Diag(Loc, diag::note_condition_or_assign_to_comparison) - << FixItHint::CreateReplacement(Loc, "!="); - else - Diag(Loc, diag::note_condition_assign_to_comparison) - << FixItHint::CreateReplacement(Loc, "=="); + // Modifying the declaration like this is friendly to IR-gen but + // also really dangerous. + VD->setType(DestType); + E->setType(Type); + E->setValueKind(ValueKind); + return E; } -void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) { - // Don't warn if the parens came from a macro. - SourceLocation parenLoc = ParenE->getBeginLoc(); - if (parenLoc.isInvalid() || parenLoc.isMacroID()) - return; - // Don't warn for dependent expressions. - if (ParenE->isTypeDependent()) - return; +ExprResult Sema::checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, + Expr *CastExpr, CastKind &CastKind, + ExprValueKind &VK, CXXCastPath &Path) { + // The type we're casting to must be either void or complete. + if (!CastType->isVoidType() && + RequireCompleteType(TypeRange.getBegin(), CastType, + diag::err_typecheck_cast_to_incomplete)) + return ExprError(); - Expr *E = ParenE->IgnoreParens(); + // Rewrite the casted expression from scratch. + ExprResult result = RebuildUnknownAnyExpr(*this, CastType).Visit(CastExpr); + if (!result.isUsable()) return ExprError(); - if (BinaryOperator *opE = dyn_cast(E)) - if (opE->getOpcode() == BO_EQ && - opE->getLHS()->IgnoreParenImpCasts()->isModifiableLvalue(Context) - == Expr::MLV_Valid) { - SourceLocation Loc = opE->getOperatorLoc(); + CastExpr = result.get(); + VK = CastExpr->getValueKind(); + CastKind = CK_NoOp; - Diag(Loc, diag::warn_equality_with_extra_parens) << E->getSourceRange(); - SourceRange ParenERange = ParenE->getSourceRange(); - Diag(Loc, diag::note_equality_comparison_silence) - << FixItHint::CreateRemoval(ParenERange.getBegin()) - << FixItHint::CreateRemoval(ParenERange.getEnd()); - Diag(Loc, diag::note_equality_comparison_to_assign) - << FixItHint::CreateReplacement(Loc, "="); - } + return CastExpr; } -ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, - bool IsConstexpr) { - DiagnoseAssignmentAsCondition(E); - if (ParenExpr *parenE = dyn_cast(E)) - DiagnoseEqualityWithExtraParens(parenE); +ExprResult Sema::forceUnknownAnyToType(Expr *E, QualType ToType) { + return RebuildUnknownAnyExpr(*this, ToType).Visit(E); +} + +ExprResult Sema::checkUnknownAnyArg(SourceLocation callLoc, + Expr *arg, QualType ¶mType) { + // If the syntactic form of the argument is not an explicit cast of + // any sort, just do default argument promotion. + ExplicitCastExpr *castArg = dyn_cast(arg->IgnoreParens()); + if (!castArg) { + ExprResult result = DefaultArgumentPromotion(arg); + if (result.isInvalid()) return ExprError(); + paramType = result.get()->getType(); + return result; + } - ExprResult result = CheckPlaceholderExpr(E); - if (result.isInvalid()) return ExprError(); - E = result.get(); + // Otherwise, use the type that was written in the explicit cast. + assert(!arg->hasPlaceholderType()); + paramType = castArg->getTypeAsWritten(); - if (!E->isTypeDependent()) { - if (getLangOpts().CPlusPlus) - return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4 + // Copy-initialize a parameter of that type. + InitializedEntity entity = + InitializedEntity::InitializeParameter(Context, paramType, + /*consumed*/ false); + return PerformCopyInitialization(entity, callLoc, arg); +} - ExprResult ERes = DefaultFunctionArrayLvalueConversion(E); - if (ERes.isInvalid()) - return ExprError(); - E = ERes.get(); +static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *E) { + Expr *orig = E; + unsigned diagID = diag::err_uncasted_use_of_unknown_any; + while (true) { + E = E->IgnoreParenImpCasts(); + if (CallExpr *call = dyn_cast(E)) { + E = call->getCallee(); + diagID = diag::err_uncasted_call_of_unknown_any; + } else { + break; + } + } - QualType T = E->getType(); - if (!T->isScalarType()) { // C99 6.8.4.1p1 - Diag(Loc, diag::err_typecheck_statement_requires_scalar) - << T << E->getSourceRange(); + SourceLocation loc; + NamedDecl *d; + if (DeclRefExpr *ref = dyn_cast(E)) { + loc = ref->getLocation(); + d = ref->getDecl(); + } else if (MemberExpr *mem = dyn_cast(E)) { + loc = mem->getMemberLoc(); + d = mem->getMemberDecl(); + } else if (ObjCMessageExpr *msg = dyn_cast(E)) { + diagID = diag::err_uncasted_call_of_unknown_any; + loc = msg->getSelectorStartLoc(); + d = msg->getMethodDecl(); + if (!d) { + S.Diag(loc, diag::err_uncasted_send_to_unknown_any_method) + << static_cast(msg->isClassMessage()) << msg->getSelector() + << orig->getSourceRange(); return ExprError(); } - CheckBoolLikeConversion(E, Loc); + } else { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) + << E->getSourceRange(); + return ExprError(); } - return E; + S.Diag(loc, diagID) << d << orig->getSourceRange(); + + // Never recoverable. + return ExprError(); } -Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, - Expr *SubExpr, ConditionKind CK, - bool MissingOK) { - // MissingOK indicates whether having no condition expression is valid - // (for loop) or invalid (e.g. while loop). - if (!SubExpr) - return MissingOK ? ConditionResult() : ConditionError(); +ExprResult Sema::CheckPlaceholderExpr(Expr *E) { + if (!Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes on either side of a binop because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + ExprResult Result = CorrectDelayedTyposInExpr(E); + if (!Result.isUsable()) return ExprError(); + E = Result.get(); + } - ExprResult Cond; - switch (CK) { - case ConditionKind::Boolean: - Cond = CheckBooleanCondition(Loc, SubExpr); - break; + const BuiltinType *placeholderType = E->getType()->getAsPlaceholderType(); + if (!placeholderType) return E; - case ConditionKind::ConstexprIf: - Cond = CheckBooleanCondition(Loc, SubExpr, true); - break; + switch (placeholderType->getKind()) { + case BuiltinType::UnresolvedTemplate: { + auto *ULE = cast(E); + const DeclarationNameInfo &NameInfo = ULE->getNameInfo(); + // There's only one FoundDecl for UnresolvedTemplate type. See + // BuildTemplateIdExpr. + NamedDecl *Temp = *ULE->decls_begin(); + const bool IsTypeAliasTemplateDecl = isa(Temp); - case ConditionKind::Switch: - Cond = CheckSwitchCondition(Loc, SubExpr); - break; - } - if (Cond.isInvalid()) { - Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), - {SubExpr}, PreferredConditionType(CK)); - if (!Cond.get()) - return ConditionError(); - } - // FIXME: FullExprArg doesn't have an invalid bit, so check nullness instead. - FullExprArg FullExpr = MakeFullExpr(Cond.get(), Loc); - if (!FullExpr.get()) - return ConditionError(); + if (NestedNameSpecifierLoc Loc = ULE->getQualifierLoc(); Loc.hasQualifier()) + Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) + << Loc.getNestedNameSpecifier() << NameInfo.getName().getAsString() + << Loc.getSourceRange() << IsTypeAliasTemplateDecl; + else + Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) + << "" << NameInfo.getName().getAsString() << ULE->getSourceRange() + << IsTypeAliasTemplateDecl; + Diag(Temp->getLocation(), diag::note_referenced_type_template) + << IsTypeAliasTemplateDecl; - return ConditionResult(*this, nullptr, FullExpr, - CK == ConditionKind::ConstexprIf); -} + return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}); + } -namespace { - /// A visitor for rebuilding a call to an __unknown_any expression - /// to have an appropriate type. - struct RebuildUnknownAnyFunction - : StmtVisitor { + // Overloaded expressions. + case BuiltinType::Overload: { + // Try to resolve a single function template specialization. + // This is obligatory. + ExprResult Result = E; + if (ResolveAndFixSingleFunctionTemplateSpecialization(Result, false)) + return Result; - Sema &S; + // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization + // leaves Result unchanged on failure. + Result = E; + if (resolveAndFixAddressOfSingleOverloadCandidate(Result)) + return Result; - RebuildUnknownAnyFunction(Sema &S) : S(S) {} + // If that failed, try to recover with a call. + tryToRecoverWithCall(Result, PDiag(diag::err_ovl_unresolvable), + /*complain*/ true); + return Result; + } - ExprResult VisitStmt(Stmt *S) { - llvm_unreachable("unexpected statement!"); + // Bound member functions. + case BuiltinType::BoundMember: { + ExprResult result = E; + const Expr *BME = E->IgnoreParens(); + PartialDiagnostic PD = PDiag(diag::err_bound_member_function); + // Try to give a nicer diagnostic if it is a bound member that we recognize. + if (isa(BME)) { + PD = PDiag(diag::err_dtor_expr_without_call) << /*pseudo-destructor*/ 1; + } else if (const auto *ME = dyn_cast(BME)) { + if (ME->getMemberNameInfo().getName().getNameKind() == + DeclarationName::CXXDestructorName) + PD = PDiag(diag::err_dtor_expr_without_call) << /*destructor*/ 0; } + tryToRecoverWithCall(result, PD, + /*complain*/ true); + return result; + } - ExprResult VisitExpr(Expr *E) { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_call) - << E->getSourceRange(); - return ExprError(); - } + // ARC unbridged casts. + case BuiltinType::ARCUnbridgedCast: { + Expr *realCast = ObjC().stripARCUnbridgedCast(E); + ObjC().diagnoseARCUnbridgedCast(realCast); + return realCast; + } - /// Rebuild an expression which simply semantically wraps another - /// expression which it shares the type and value kind of. - template ExprResult rebuildSugarExpr(T *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); + // Expressions of unknown type. + case BuiltinType::UnknownAny: + return diagnoseUnknownAnyExpr(*this, E); - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(SubExpr->getType()); - E->setValueKind(SubExpr->getValueKind()); - assert(E->getObjectKind() == OK_Ordinary); - return E; - } + // Pseudo-objects. + case BuiltinType::PseudoObject: + return PseudoObject().checkRValue(E); - ExprResult VisitParenExpr(ParenExpr *E) { - return rebuildSugarExpr(E); - } + case BuiltinType::BuiltinFn: { + // Accept __noop without parens by implicitly converting it to a call expr. + auto *DRE = dyn_cast(E->IgnoreParenImpCasts()); + if (DRE) { + auto *FD = cast(DRE->getDecl()); + unsigned BuiltinID = FD->getBuiltinID(); + if (BuiltinID == Builtin::BI__noop) { + E = ImpCastExprToType(E, Context.getPointerType(FD->getType()), + CK_BuiltinFnToFnPtr) + .get(); + return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy, + VK_PRValue, SourceLocation(), + FPOptionsOverride()); + } - ExprResult VisitUnaryExtension(UnaryOperator *E) { - return rebuildSugarExpr(E); + if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) { + // Any use of these other than a direct call is ill-formed as of C++20, + // because they are not addressable functions. In earlier language + // modes, warn and force an instantiation of the real body. + Diag(E->getBeginLoc(), + getLangOpts().CPlusPlus20 + ? diag::err_use_of_unaddressable_function + : diag::warn_cxx20_compat_use_of_unaddressable_function); + if (FD->isImplicitlyInstantiable()) { + // Require a definition here because a normal attempt at + // instantiation for a builtin will be ignored, and we won't try + // again later. We assume that the definition of the template + // precedes this use. + InstantiateFunctionDefinition(E->getBeginLoc(), FD, + /*Recursive=*/false, + /*DefinitionRequired=*/true, + /*AtEndOfTU=*/false); + } + // Produce a properly-typed reference to the function. + CXXScopeSpec SS; + SS.Adopt(DRE->getQualifierLoc()); + TemplateArgumentListInfo TemplateArgs; + DRE->copyTemplateArgumentsInto(TemplateArgs); + return BuildDeclRefExpr( + FD, FD->getType(), VK_LValue, DRE->getNameInfo(), + DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(), + DRE->getTemplateKeywordLoc(), + DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr); + } } - ExprResult VisitUnaryAddrOf(UnaryOperator *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); - - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(S.Context.getPointerType(SubExpr->getType())); - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); - return E; - } + Diag(E->getBeginLoc(), diag::err_builtin_fn_use); + return ExprError(); + } - ExprResult resolveDecl(Expr *E, ValueDecl *VD) { - if (!isa(VD)) return VisitExpr(E); + case BuiltinType::IncompleteMatrixIdx: + Diag(cast(E->IgnoreParens()) + ->getRowIdx() + ->getBeginLoc(), + diag::err_matrix_incomplete_index); + return ExprError(); - E->setType(VD->getType()); + // Expressions of unknown type. + case BuiltinType::ArraySection: + Diag(E->getBeginLoc(), diag::err_array_section_use) + << cast(E)->isOMPArraySection(); + return ExprError(); - assert(E->isPRValue()); - if (S.getLangOpts().CPlusPlus && - !(isa(VD) && - cast(VD)->isInstance())) - E->setValueKind(VK_LValue); + // Expressions of unknown type. + case BuiltinType::OMPArrayShaping: + return ExprError(Diag(E->getBeginLoc(), diag::err_omp_array_shaping_use)); - return E; - } + case BuiltinType::OMPIterator: + return ExprError(Diag(E->getBeginLoc(), diag::err_omp_iterator_use)); - ExprResult VisitMemberExpr(MemberExpr *E) { - return resolveDecl(E, E->getMemberDecl()); - } + // Everything else should be impossible. +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" +#define PPC_VECTOR_TYPE(Name, Id, Size) \ + case BuiltinType::Id: +#include "clang/Basic/PPCTypes.def" +#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/RISCVVTypes.def" +#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/WebAssemblyReferenceTypes.def" +#define AMDGPU_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/AMDGPUTypes.def" +#define BUILTIN_TYPE(Id, SingletonId) case BuiltinType::Id: +#define PLACEHOLDER_TYPE(Id, SingletonId) +#include "clang/AST/BuiltinTypes.def" + break; + } - ExprResult VisitDeclRefExpr(DeclRefExpr *E) { - return resolveDecl(E, E->getDecl()); - } - }; + llvm_unreachable("invalid placeholder type!"); } -/// Given a function expression of unknown-any type, try to rebuild it -/// to have a function type. -static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *FunctionExpr) { - ExprResult Result = RebuildUnknownAnyFunction(S).Visit(FunctionExpr); - if (Result.isInvalid()) return ExprError(); - return S.DefaultFunctionArrayConversion(Result.get()); +bool Sema::CheckCaseExpression(Expr *E) { + if (E->isTypeDependent()) + return true; + if (E->isValueDependent() || E->isIntegerConstantExpr(Context)) + return E->getType()->isIntegralOrEnumerationType(); + return false; } -namespace { - /// A visitor for rebuilding an expression of type __unknown_anytype - /// into one which resolves the type directly on the referring - /// expression. Strict preservation of the original source - /// structure is not a goal. - struct RebuildUnknownAnyExpr - : StmtVisitor { - - Sema &S; - - /// The current destination type. - QualType DestType; - - RebuildUnknownAnyExpr(Sema &S, QualType CastType) - : S(S), DestType(CastType) {} +ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, + ArrayRef SubExprs, QualType T) { + if (!Context.getLangOpts().RecoveryAST) + return ExprError(); - ExprResult VisitStmt(Stmt *S) { - llvm_unreachable("unexpected statement!"); - } + if (isSFINAEContext()) + return ExprError(); - ExprResult VisitExpr(Expr *E) { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) - << E->getSourceRange(); - return ExprError(); - } + if (T.isNull() || T->isUndeducedType() || + !Context.getLangOpts().RecoveryASTType) + // We don't know the concrete type, fallback to dependent type. + T = Context.DependentTy; - ExprResult VisitCallExpr(CallExpr *E); - ExprResult VisitObjCMessageExpr(ObjCMessageExpr *E); + return RecoveryExpr::Create(Context, T, Begin, End, SubExprs); +} - /// Rebuild an expression which simply semantically wraps another - /// expression which it shares the type and value kind of. - template ExprResult rebuildSugarExpr(T *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(SubExpr->getType()); - E->setValueKind(SubExpr->getValueKind()); - assert(E->getObjectKind() == OK_Ordinary); - return E; - } +/* TO_UPSTREAM(BoundsSafety) ON*/ +ExprResult Sema::ActOnForgeBidiIndexable(SourceLocation KWLoc, + Expr *Addr, Expr *Size, + SourceLocation RParenLoc) { + BoundsSafetyPointerAttributes Att; + Att.setBidiIndexable(); + QualType ResultType = Context.getPointerType(Context.VoidTy, Att); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, Size); +} - ExprResult VisitParenExpr(ParenExpr *E) { - return rebuildSugarExpr(E); - } +ExprResult Sema::ActOnForgeSingle(SourceLocation KWLoc, Expr *Addr, + SourceLocation RParenLoc) { + QualType ResultType; + if (getLangOpts().isBoundsSafetyAttributeOnlyMode()) { + ResultType = Context.getAttributedType(attr::PtrSingle, Context.VoidPtrTy, + Context.VoidPtrTy); + } else + ResultType = Context.getPointerType(Context.VoidTy, + BoundsSafetyPointerAttributes::single()); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr); +} - ExprResult VisitUnaryExtension(UnaryOperator *E) { - return rebuildSugarExpr(E); - } +ExprResult Sema::ActOnForgeTerminatedBy(SourceLocation KWLoc, Expr *Addr, + Expr *Term, SourceLocation RParenLoc) { + BoundsSafetyPointerAttributes Att; + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode()) + Att.setSingle(); + QualType ResultType = Context.getPointerType(Context.VoidTy, Att); + ResultType = Context.getValueTerminatedType(ResultType, Term); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, /*Size*/ nullptr, + Term); +} - ExprResult VisitUnaryAddrOf(UnaryOperator *E) { - const PointerType *Ptr = DestType->getAs(); - if (!Ptr) { - S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof) - << E->getSourceRange(); - return ExprError(); - } +ExprResult Sema::ActOnBoundsSafetyCall(ExprResult Call) { + CallExpr *CallE = dyn_cast_or_null(Call.get()); + if (!CallE || CallE->containsErrors()) + return Call; - if (isa(E->getSubExpr())) { - S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof_call) - << E->getSourceRange(); - return ExprError(); + // Bail out early if there's nothing to do. (There is something to do if + // there's a dynamic bound pointer type in this function prototype, or if + // it's annotated with `alloc_size`, in which case we can derive the + // equivalent.) + QualType FT = CallE->getCallee()->getType(); + if (auto PointerTy = FT->getAs()) + FT = PointerTy->getPointeeType(); + if (!ContainsBoundsAttributedType::check(FT)) { + auto *FDecl = CallE->getDirectCallee(); + if (!FDecl || !FDecl->getAttr()) + return CallE; + } + + Expr *ResultExpr = CallE; + SmallVector OVEs; + + llvm::DenseMap> + DependentValues; + auto AddDependentParams = [&](const BoundsAttributedType *DBPT) { + for (const auto &DI : DBPT->dependent_decls()) { + if (const auto *PVD = dyn_cast(DI.getDecl())) { + unsigned Index = PVD->getFunctionScopeIndex(); + assert(Index < OVEs.size()); + Expr *Arg = OVEs[Index]; + DependentValues[PVD] = {Arg, /*Level=*/0}; } - - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); - E->setType(DestType); - - // Build the sub-expression as if it were an object of the pointee type. - DestType = Ptr->getPointeeType(); - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); - E->setSubExpr(SubResult.get()); - return E; } + }; - ExprResult VisitImplicitCastExpr(ImplicitCastExpr *E); + ReplaceDeclRefWithRHS Transform(*this, DependentValues); + + if (const FunctionProtoType *FPT = FT->getAs()) { + // Move function arguments into opaque value expressions so that we can + // refer to them without funny reordered/duplicated side effects business. + // Since there's an implicit cast to __single bounds wherever we use a + // dynamic bound pointer type, we pull the inner value of the -fbounds-safety + // pointer cast into the OVE only (and replace the -fbounds-safety pointer cast + // operand with the new OVE). + for (unsigned I = 0; I < CallE->getNumArgs(); ++I) { + Expr *Arg = CallE->getArg(I); + ImplicitCastExpr *IC = nullptr; + do { + auto *Cast = dyn_cast(Arg); + if (Cast && Cast->getCastKind() == CK_BoundsSafetyPointerCast) { + IC = Cast; + Arg = IC->getSubExpr(); + } else { + break; + } + } while (true); + auto *OVE = OpaqueValueExpr::EnsureWrapped(Context, Arg, OVEs); + if (IC) + IC->setSubExpr(OVE); + else + CallE->setArg(I, OVE); + } - ExprResult resolveDecl(Expr *E, ValueDecl *VD); + // Do we need to make any adjustments to arguments? + auto ParamTypes = FPT->getParamTypes(); + // (there are more arguments than parameters in the case of variadic + // functions, however variadic arguments are never passed as dynamic-bound + // pointers.) + assert(ParamTypes.size() <= OVEs.size()); - ExprResult VisitMemberExpr(MemberExpr *E) { - return resolveDecl(E, E->getMemberDecl()); + for (QualType ParamType : ParamTypes) { + if (const auto *DBPT = ParamType->getAs()) + AddDependentParams(DBPT); } + if (const auto *DBPT = + ResultExpr->getType()->getAs()) + AddDependentParams(DBPT); - ExprResult VisitDeclRefExpr(DeclRefExpr *E) { - return resolveDecl(E, E->getDecl()); + for (unsigned I = 0; I < ParamTypes.size(); ++I) { + if (allowBoundsUnsafeFunctionArg(CallE, I)) { + assert( + SourceMgr.isInSystemHeader(OVEs[I]->getSourceExpr()->getExprLoc())); + continue; + } + QualType ParamType = ParamTypes[I]; + BoundsCheckBuilder Builder(OVEs); + if (auto *DCPT = ParamType->getAs()) { + ExprResult CountExpr = + Transform.TransformBoundsAttrExpr(DCPT->getCountExpr()); + if (!CountExpr.get()) + return ExprError(); + Builder.setAccessCount(CountExpr.get(), DCPT->isCountInBytes(), + DCPT->isOrNull()); + } else if (auto DRPT = ParamType->getAs()) { + // The end pointer itself may be a dynamic range pointer type, but it + // will not have an end pointer, just a start pointer. The start pointer + // will generate the entire breadth of required checks. + if (auto *EndPtr = DRPT->getEndPointer()) { + ExprResult Upper = Transform.TransformBoundsAttrExpr(EndPtr); + if (!Upper.get()) + return ExprError(); + Builder.setAccessLowerBound(OVEs[I]); + Builder.setAccessUpperBound(Upper.get()); + } else { + continue; + } + } else { + continue; + } + Builder.setWidePointer(OVEs[I]); + ExprResult R = Builder.Build(*this, ResultExpr); + if (!(ResultExpr = R.get())) + return ExprError(); } - }; -} - -/// Rebuilds a call expression which yielded __unknown_anytype. -ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) { - Expr *CalleeExpr = E->getCallee(); - - enum FnKind { - FK_MemberFunction, - FK_FunctionPointer, - FK_BlockPointer - }; - - FnKind Kind; - QualType CalleeType = CalleeExpr->getType(); - if (CalleeType == S.Context.BoundMemberTy) { - assert(isa(E) || isa(E)); - Kind = FK_MemberFunction; - CalleeType = Expr::findBoundMemberType(CalleeExpr); - } else if (const PointerType *Ptr = CalleeType->getAs()) { - CalleeType = Ptr->getPointeeType(); - Kind = FK_FunctionPointer; - } else { - CalleeType = CalleeType->castAs()->getPointeeType(); - Kind = FK_BlockPointer; - } - const FunctionType *FnType = CalleeType->castAs(); - - // Verify that this is a legal result type of a function. - if (DestType->isArrayType() || DestType->isFunctionType()) { - unsigned diagID = diag::err_func_returning_array_function; - if (Kind == FK_BlockPointer) - diagID = diag::err_block_returning_array_function; - - S.Diag(E->getExprLoc(), diagID) - << DestType->isFunctionType() << DestType; - return ExprError(); } - // Otherwise, go ahead and set DestType as the call's result. - E->setType(DestType.getNonLValueExprType(S.Context)); - E->setValueKind(Expr::getValueKindForType(DestType)); - assert(E->getObjectKind() == OK_Ordinary); + // Do we need to make any adjustments to the return value? This needs to also + // account for attributes on the function which specify that the function + // allocated memory (in addition to common dynamic bound pointer types), and + // for flexible array member pointers. + QualType ResultTy = ResultExpr->getType(); + if (auto *DCPT = ResultTy->getAs()) { + ExprResult ReturnCount = + Transform.TransformBoundsAttrExpr(DCPT->getCountExpr()); + if (!ReturnCount.get()) + return ExprError(); + ReturnCount = DefaultLvalueConversion(ReturnCount.get()); + if (!ReturnCount.get()) + return ExprError(); + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, ResultExpr, OVEs); + auto *CountOVE = OpaqueValueExpr::EnsureWrapped( + Context, ReturnCount.get(), OVEs); + ExprResult Promoted = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, CountOVE, DCPT->isCountInBytes(), DCPT->isOrNull()); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *DRPT = ResultTy->getAs()) { + ExprResult Upper = Transform.TransformBoundsAttrExpr(DRPT->getEndPointer()); + if (!Upper.get()) + return ExprError(); + auto FA = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType FPtrTy = Context.getPointerType(DRPT->getPointeeType(), FA); + ExprResult Promoted = BoundsSafetyPointerPromotionExpr::Create( + Context, FPtrTy, ResultExpr, Upper.get()); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *RD = getImmediateDeclForFlexibleArrayPromotion(ResultTy)) { + ExprResult Promoted = PromoteBoundsSafetyPointerToFlexibleArrayMember( + *this, RD, ResultExpr); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *FDecl = CallE->getDirectCallee()) { + if (auto Att = FDecl->getAttr()) { + if (Att->getElemSizeParam().isValid()) { + Expr *SizeExpr = OVEs[Att->getElemSizeParam().getASTIndex()]; + if (Att->getNumElemsParam().isValid()) { + Expr *CountExpr = OVEs[Att->getNumElemsParam().getASTIndex()]; + ExprResult Product = CreateBuiltinBinOp( + CallE->getBeginLoc(), BO_Mul, SizeExpr, CountExpr); + if (!(SizeExpr = Product.get())) + return ExprError(); + } - // Rebuild the function type, replacing the result type with DestType. - const FunctionProtoType *Proto = dyn_cast(FnType); - if (Proto) { - // __unknown_anytype(...) is a special case used by the debugger when - // it has no idea what a function's signature is. - // - // We want to build this call essentially under the K&R - // unprototyped rules, but making a FunctionNoProtoType in C++ - // would foul up all sorts of assumptions. However, we cannot - // simply pass all arguments as variadic arguments, nor can we - // portably just call the function under a non-variadic type; see - // the comment on IR-gen's TargetInfo::isNoProtoCallVariadic. - // However, it turns out that in practice it is generally safe to - // call a function declared as "A foo(B,C,D);" under the prototype - // "A foo(B,C,D,...);". The only known exception is with the - // Windows ABI, where any variadic function is implicitly cdecl - // regardless of its normal CC. Therefore we change the parameter - // types to match the types of the arguments. - // - // This is a hack, but it is far superior to moving the - // corresponding target-specific code from IR-gen to Sema/AST. + ExprResult SizeRValue = DefaultLvalueConversion(SizeExpr); + if (!SizeRValue.get()) + return ExprError(); - ArrayRef ParamTypes = Proto->getParamTypes(); - SmallVector ArgTypes; - if (ParamTypes.empty() && Proto->isVariadic()) { // the special case - ArgTypes.reserve(E->getNumArgs()); - for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { - ArgTypes.push_back(S.Context.getReferenceQualifiedType(E->getArg(i))); + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, ResultExpr, OVEs); + auto *SizeOVE = OpaqueValueExpr::EnsureWrapped( + Context, SizeRValue.get(), OVEs); + ExprResult Promoted = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, SizeOVE, true, true); + if (!(ResultExpr = Promoted.get())) + return ExprError(); } - ParamTypes = ArgTypes; } - DestType = S.Context.getFunctionType(DestType, ParamTypes, - Proto->getExtProtoInfo()); - } else { - DestType = S.Context.getFunctionNoProtoType(DestType, - FnType->getExtInfo()); } - // Rebuild the appropriate pointer-to-function type. - switch (Kind) { - case FK_MemberFunction: - // Nothing to do. - break; + if (!OVEs.empty()) { + // Put opaque value expressions in materialize expressions and return that. + ResultExpr = MaterializeSequenceExpr::Create(Context, ResultExpr, OVEs); + ResultExpr = MaterializeSequenceExpr::Create(Context, ResultExpr, OVEs, true); + } + return ResultExpr; +} - case FK_FunctionPointer: - DestType = S.Context.getPointerType(DestType); - break; +ExprResult Sema::BuildForgePtrExpr(SourceLocation KWLoc, + SourceLocation RParenLoc, + QualType ResultType, Expr *Addr, Expr *Size, + Expr *Terminator) { + BoundsSafetyPointerAttributes Att = + ResultType->getAs()->getPointerAttributes(); + unsigned IsForgeTerminated = ResultType->isValueTerminatedType(); + unsigned IsForgeSingle = !Att.hasUpperBound() && !IsForgeTerminated; + // Ensure that there is a size if we're forging a bidi pointer, and that + // there isn't one otherwise. In the same way there should be a terminator + // value iff the type is __terminated_by. + assert(IsForgeTerminated == (Terminator != nullptr)); + assert(IsForgeSingle == ((Size == nullptr) && !IsForgeTerminated)); - case FK_BlockPointer: - DestType = S.Context.getBlockPointerType(DestType); - break; + ExprResult RvalueAddr = + DefaultFunctionArrayLvalueConversion(Addr, true, false); + if (RvalueAddr.isInvalid()) + return ExprError(); + Addr = RvalueAddr.get(); + + if (!Addr->isTypeDependent()) { + unsigned ForgeType = 0; // ForgeBidi + if (IsForgeSingle) + ForgeType = 1; + else if (IsForgeTerminated) + ForgeType = 2; + + if (Addr->getType()->isIntegralType(Context)) { + Expr::EvalResult EvalAddr; + if (Addr->EvaluateAsInt(EvalAddr, Context, Expr::SE_AllowSideEffects) && + EvalAddr.Val.getInt() < 0) { + Diag(Addr->getBeginLoc(), diag::err_bounds_safety_forge_negative) + << 0 << ForgeType; + return ExprError(); + } + } else if (!Addr->getType()->isPointerType()) { + Diag(Addr->getBeginLoc(), diag::err_bounds_safety_forge_addr_type) + << ForgeType; + return ExprError(); + } } - // Finally, we can recurse. - ExprResult CalleeResult = Visit(CalleeExpr); - if (!CalleeResult.isUsable()) return ExprError(); - E->setCallee(CalleeResult.get()); + ExprResult RvalueSize = ExprEmpty(); + if (Size) { + RvalueSize = DefaultLvalueConversion(Size); + if (RvalueSize.isInvalid()) + return ExprError(); - // Bind a temporary if necessary. - return S.MaybeBindToTemporary(E); -} + if (!RvalueSize.get()->getType()->isIntegralType(Context)) { + Diag(Size->getBeginLoc(), diag::err_bounds_safety_forge_bidi_size_type); + return ExprError(); + } -ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *E) { - // Verify that this is a legal result type of a call. - if (DestType->isArrayType() || DestType->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_func_returning_array_function) - << DestType->isFunctionType() << DestType; - return ExprError(); + // The size value is treated as signed in CodeGen regardless of type. By casting + // values to a wider type if possible we avoid the issue of the upper half of the range + // of unsigned types being interpreted as negative values. + RvalueSize = ImpCastExprToType(RvalueSize.get(), Context.getSizeType(), clang::CK_IntegralCast); + if (RvalueSize.isInvalid()) + return ExprError(); + + Expr::EvalResult EvalSize; + if (RvalueSize.get()->EvaluateAsInt(EvalSize, Context, + Expr::SE_AllowSideEffects) && + EvalSize.Val.getInt().APInt::isNegative()) { + // An unsigned value that is bigger than SIZE_MAX/2 ultimately + // becomes a negative integer in CodeGen. Thus, we prevent it as + // a negative size. + Diag(Size->getBeginLoc(), diag::err_bounds_safety_forge_negative) + << 1 << IsForgeSingle; + return ExprError(); + } } - // Rewrite the method result type if available. - if (ObjCMethodDecl *Method = E->getMethodDecl()) { - assert(Method->getReturnType() == S.Context.UnknownAnyTy); - Method->setReturnType(DestType); + ExprResult RvalueTerm = ExprEmpty(); + if (Terminator) { + RvalueTerm = DefaultLvalueConversion(Terminator); + if (RvalueTerm.isInvalid()) + return ExprError(); } - // Change the type of the message. - E->setType(DestType.getNonReferenceType()); - E->setValueKind(Expr::getValueKindForType(DestType)); + ExprValueKind VK = Expr::getValueKindForType(ResultType); + return new (Context) + ForgePtrExpr(ResultType, VK, RvalueAddr.get(), RvalueSize.get(), + RvalueTerm.get(), KWLoc, RParenLoc); +} + +static ExprResult makeBoundExpr(Sema& S, Expr *SubExpr, + GetBoundExpr::BoundKind BK, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc, + bool RawPointer) { + // make sure the expression is an rvalue + ExprResult RValue = S.DefaultFunctionArrayLvalueConversion(SubExpr); + if (RValue.isInvalid()) + return ExprError(); + SubExpr = RValue.get(); - return S.MaybeBindToTemporary(E); -} + // result type is __bidi_indexable + QualType T = SubExpr->getType(); + if (T->isDependentType()) + return ExprError(); -ExprResult RebuildUnknownAnyExpr::VisitImplicitCastExpr(ImplicitCastExpr *E) { - // The only case we should ever see here is a function-to-pointer decay. - if (E->getCastKind() == CK_FunctionToPointerDecay) { - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); + int Reason = -1; + const PointerType *PT = T->getAs(); + if (!PT) { + Reason = 0; + } else if (PT->getPointerAttributes().isUnsafeOrUnspecified()) { + Reason = 1; + } - E->setType(DestType); + if (Reason >= 0) { + S.Diag(BuiltinLoc, diag::err_bounds_safety_no_bounds) << BK << T << Reason; + return ExprError(); + } - // Rebuild the sub-expression as the pointee (function) type. - DestType = DestType->castAs()->getPointeeType(); + auto FA = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType RT = S.Context.getPointerType(PT->getPointeeType(), FA); - ExprResult Result = Visit(E->getSubExpr()); - if (!Result.isUsable()) return ExprError(); + // must implicitly cast original to bidi_indexable if needed + if (PT->getPointerAttributes() != FA) { + if (!PT->isSafePointerType()) + return ExprError(); + ExprResult ImpCast = S.ImpCastExprToType( + SubExpr, RT, CK_BoundsSafetyPointerCast); + if (ImpCast.isInvalid()) + return ExprError(); + SubExpr = ImpCast.get(); + } - E->setSubExpr(Result.get()); - return E; - } else if (E->getCastKind() == CK_LValueToRValue) { - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); + auto &Ctx = S.Context; + QualType DstTy = RawPointer ? Ctx.getPointerType(PT->getPointeeType()) : RT; + return new (Ctx) GetBoundExpr(BuiltinLoc, RParenLoc, SubExpr, BK, DstTy); +} - assert(isa(E->getType())); +ExprResult Sema::BuildLowerBoundExpr(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc, bool RawPointer) { + return makeBoundExpr(*this, SubExpr, GetBoundExpr::BK_Lower, BuiltinLoc, + RParenLoc, RawPointer); +} - E->setType(DestType); +ExprResult Sema::ActOnGetLowerBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildLowerBoundExpr(SubExpr, BuiltinLoc, RParenLoc); +} - // The sub-expression has to be a lvalue reference, so rebuild it as such. - DestType = S.Context.getLValueReferenceType(DestType); +ExprResult Sema::BuildUpperBoundExpr(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc, bool RawPointer) { + return makeBoundExpr(*this, SubExpr, GetBoundExpr::BK_Upper, BuiltinLoc, + RParenLoc, RawPointer); +} - ExprResult Result = Visit(E->getSubExpr()); - if (!Result.isUsable()) return ExprError(); +ExprResult Sema::ActOnGetUpperBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildUpperBoundExpr(SubExpr, BuiltinLoc, RParenLoc); +} - E->setSubExpr(Result.get()); - return E; - } else { - llvm_unreachable("Unhandled cast type!"); - } +ExprResult Sema::BuildPredefinedBoundsCheckExpr(Expr *SubExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) { + return PredefinedBoundsCheckExpr::Create(Context, SubExpr, Kind, CheckArgs); } -ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) { - ExprValueKind ValueKind = VK_LValue; - QualType Type = DestType; +ExprResult Sema::BuildBoundsCheckExpr(Expr *SubExpr, Expr *Cond, + ArrayRef CommonExprs) { + // FIXME: Bounds expressions would better have implicit casts similar to + // what we would get from Sema::CheckCompareOperands(). This, however, + // would insert implicit casts of wide pointer to raw pointer. This might not + // be matching what GetBoundsExpr CodeGen currently expects. + return BoundsCheckExpr::Create(Context, SubExpr, Cond, CommonExprs); +} - // We know how to make this work for certain kinds of decls: +ExprResult Sema::BuildMaterializeSequenceExpr(Expr *WrappedExpr, + ArrayRef Values) { + return MaterializeSequenceExpr::Create(Context, WrappedExpr, Values); +} - // - functions - if (FunctionDecl *FD = dyn_cast(VD)) { - if (const PointerType *Ptr = Type->getAs()) { - DestType = Ptr->getPointeeType(); - ExprResult Result = resolveDecl(E, VD); - if (Result.isInvalid()) return ExprError(); - return S.ImpCastExprToType(Result.get(), Type, CK_FunctionToPointerDecay, - VK_PRValue); - } +ExprResult BoundsCheckBuilder::BuildImplicitPointerArith(Sema &S, + BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS) { + QualType PointerTy = LHS->getType(); + assert(Opc == BO_Add || Opc == BO_Sub); + assert(PointerTy->isPointerType() && + RHS->getType()->isIntegralOrEnumerationType()); - if (!Type->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_unknown_any_function) - << VD << E->getSourceRange(); + Expr *Pointer = LHS; + + if (PointerTy->isSinglePointerType() && !PointerTy->isBoundsAttributedType()) { + BoundsSafetyPointerAttributes UnsafeAttr; + UnsafeAttr.setUnsafeIndexable(); + QualType UnsafePointerTy = S.Context.getBoundsSafetyPointerType(PointerTy, UnsafeAttr); + ExprResult CastResult = S.BuildCStyleCastExpr(SourceLocation(), S.Context.getTrivialTypeSourceInfo(UnsafePointerTy), SourceLocation(), Pointer); + if (CastResult.isInvalid()) return ExprError(); - } - if (const FunctionProtoType *FT = Type->getAs()) { - // We must match the FunctionDecl's type to the hack introduced in - // RebuildUnknownAnyExpr::VisitCallExpr to vararg functions of unknown - // type. See the lengthy commentary in that routine. - QualType FDT = FD->getType(); - const FunctionType *FnType = FDT->castAs(); - const FunctionProtoType *Proto = dyn_cast_or_null(FnType); - DeclRefExpr *DRE = dyn_cast(E); - if (DRE && Proto && Proto->getParamTypes().empty() && Proto->isVariadic()) { - SourceLocation Loc = FD->getLocation(); - FunctionDecl *NewFD = FunctionDecl::Create( - S.Context, FD->getDeclContext(), Loc, Loc, - FD->getNameInfo().getName(), DestType, FD->getTypeSourceInfo(), - SC_None, S.getCurFPFeatures().isFPConstrained(), - false /*isInlineSpecified*/, FD->hasPrototype(), - /*ConstexprKind*/ ConstexprSpecKind::Unspecified); - if (FD->getQualifier()) - NewFD->setQualifierInfo(FD->getQualifierLoc()); + Pointer = CastResult.get(); + } - SmallVector Params; - for (const auto &AI : FT->param_types()) { - ParmVarDecl *Param = - S.BuildParmVarDeclForTypedef(FD, Loc, AI); - Param->setScopeInfo(0, Params.size()); - Params.push_back(Param); - } - NewFD->setParams(Params); - DRE->setDecl(NewFD); - VD = DRE->getDecl(); - } - } + return S.CreateBuiltinBinOp(SourceLocation(), Opc, Pointer, RHS); +} - if (CXXMethodDecl *MD = dyn_cast(FD)) - if (MD->isInstance()) { - ValueKind = VK_PRValue; - Type = S.Context.BoundMemberTy; - } +OpaqueValueExpr *BoundsCheckBuilder::OpaqueWrap(Sema &S, Expr *E) { + return OpaqueValueExpr::EnsureWrapped(S.Context, E, OVEs); +} - // Function references aren't l-values in C. - if (!S.getLangOpts().CPlusPlus) - ValueKind = VK_PRValue; +bool BoundsCheckBuilder::BuildIndexBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &R) { + if (isCountInBytes()) { + ExprResult Lower = + CastToCharPointer(S, Min, BoundsSafetyPointerAttributes::unspecified()); + if (!(Min = Lower.get())) + return false; + ExprResult Upper = + CastToCharPointer(S, Max, BoundsSafetyPointerAttributes::unspecified()); + if (!(Max = Upper.get())) + return false; + } + ExprResult Count = S.CreateBuiltinBinOp(Max->getBeginLoc(), BO_Sub, Max, Min); + if (!Count.get()) + return false; - // - variables - } else if (isa(VD)) { - if (const ReferenceType *RefTy = Type->getAs()) { - Type = RefTy->getPointeeType(); - } else if (Type->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_unknown_any_var_function_type) - << VD << E->getSourceRange(); - return ExprError(); - } + bool IsSigned = false; - // - nothing else + Expr *AccessCount; + QualType SizeT = S.Context.getSizeType(); + if (auto *Idx = getAccessCount()) { + AccessCount = Idx; + IsSigned |= AccessCount->getType()->isSignedIntegerOrEnumerationType(); } else { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_decl) - << VD << E->getSourceRange(); - return ExprError(); + Expr *One = S.ActOnIntegerConstant(Min->getBeginLoc(), 1).get(); + ExprResult R = S.ImpCastExprToType(One, SizeT, CK_IntegralCast); + if (!(AccessCount = R.get())) + return false; } - // Modifying the declaration like this is friendly to IR-gen but - // also really dangerous. - VD->setType(DestType); - E->setType(Type); - E->setValueKind(ValueKind); - return E; -} + if (auto *StartIndex = getAccessStartIndex()) { + Expr *OpaqueStart = OpaqueWrap(S, StartIndex); + R.push_back(OpaqueStart); + IsSigned |= OpaqueStart->getType()->isSignedIntegerOrEnumerationType(); + ExprResult CountTotal = S.CreateBuiltinBinOp( + OpaqueStart->getBeginLoc(), BO_Add, OpaqueStart, AccessCount); + if (!(AccessCount = CountTotal.get())) + return false; + } + R.push_back(AccessCount); + + if (IsSigned) { + // If any value in R is signed, cast all of them to signed values and + // add a check for zero at first. + for (size_t I = 0; I < R.size(); ++I) { + Expr *&Elem = R[I]; + QualType Ty = Elem->getType(); + if (!Ty->isSignedIntegerOrEnumerationType()) { + QualType SignedType = S.Context.getCorrespondingSignedType(Ty); + ExprResult R = S.ImpCastExprToType(Elem, SignedType, CK_IntegralCast); + if (!(Elem = R.get())) + return false; + } + } -ExprResult Sema::checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, - Expr *CastExpr, CastKind &CastKind, - ExprValueKind &VK, CXXCastPath &Path) { - // The type we're casting to must be either void or complete. - if (!CastType->isVoidType() && - RequireCompleteType(TypeRange.getBegin(), CastType, - diag::err_typecheck_cast_to_incomplete)) - return ExprError(); + Expr *Zero = S.ActOnIntegerConstant(Min->getBeginLoc(), 0).get(); + R.insert(R.begin(), Zero); + R.push_back(Count.get()); + } else { + auto SizeTCount = S.ImpCastExprToType(Count.get(), SizeT, CK_IntegralCast); + if (Expr *C = SizeTCount.get()) + R.push_back(C); + else + return false; + } + return true; +} - // Rewrite the casted expression from scratch. - ExprResult result = RebuildUnknownAnyExpr(*this, CastType).Visit(CastExpr); - if (!result.isUsable()) return ExprError(); +bool BoundsCheckBuilder::BuildPtrBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &R) { + // second, expand the accessed lower bound + Expr *TestedLowerBound; + if (auto *Lower = getAccessLowerBound()) { + TestedLowerBound = Lower; + } else { + Min = OpaqueWrap(S, Min); + Expr *LowerBound = Min; + if (auto *StartIndex = getAccessStartIndex()) { + ExprResult TLBResult = BuildImplicitPointerArith( + S, BO_Add, LowerBound, StartIndex); + if (TLBResult.isInvalid()) + return false; - CastExpr = result.get(); - VK = CastExpr->getValueKind(); - CastKind = CK_NoOp; + TestedLowerBound = TLBResult.get(); + } else { + Min = OpaqueWrap(S, Min); + TestedLowerBound = Min; + } + } - return CastExpr; -} + // next, expand the accessed upper bound + Expr *TestedUpperBound; + if (auto *Upper = getAccessUpperBound()) { + TestedUpperBound = Upper; + } else { + TestedLowerBound = OpaqueWrap(S, TestedLowerBound); + ExprResult TLB = TestedLowerBound; + ExprResult TUB; + if (auto *Count = getAccessCount()) { + // If OrigPtrTy is set, it means that we must do pointer arithmetic over a + // character type instead of the original type (and that the result must + // be cast back t OrigPtrTy). + QualType OrigPtrTy; + if (isCountInBytes()) { + QualType PtrTy = TestedLowerBound->getType(); + QualType Pointee = PtrTy->getPointeeType(); + if (S.Context.getTypeSizeInChars(Pointee).getQuantity() != 1) { + OrigPtrTy = PtrTy; + } + } -ExprResult Sema::forceUnknownAnyToType(Expr *E, QualType ToType) { - return RebuildUnknownAnyExpr(*this, ToType).Visit(E); -} + if (!OrigPtrTy.isNull()) { + TLB = CastToCharPointer(S, TLB.get()); + if (!TLB.get()) + return false; + } + TUB = BuildImplicitPointerArith(S, BO_Add, TLB.get(), Count); + if (!OrigPtrTy.isNull()) { + SourceLocation Loc = TUB.get()->getBeginLoc(); + TUB = S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(OrigPtrTy), Loc, TUB.get()); + } + } else { + // Assume count is 1. + Expr *One = S.ActOnIntegerConstant(TLB.get()->getBeginLoc(), 1).get(); + TUB = BuildImplicitPointerArith(S, BO_Add, TLB.get(), One); + } + if (TUB.isInvalid()) + return false; -ExprResult Sema::checkUnknownAnyArg(SourceLocation callLoc, - Expr *arg, QualType ¶mType) { - // If the syntactic form of the argument is not an explicit cast of - // any sort, just do default argument promotion. - ExplicitCastExpr *castArg = dyn_cast(arg->IgnoreParens()); - if (!castArg) { - ExprResult result = DefaultArgumentPromotion(arg); - if (result.isInvalid()) return ExprError(); - paramType = result.get()->getType(); - return result; + TestedUpperBound = TUB.get(); } - // Otherwise, use the type that was written in the explicit cast. - assert(!arg->hasPlaceholderType()); - paramType = castArg->getTypeAsWritten(); - - // Copy-initialize a parameter of that type. - InitializedEntity entity = - InitializedEntity::InitializeParameter(Context, paramType, - /*consumed*/ false); - return PerformCopyInitialization(entity, callLoc, arg); + R.push_back(Min); + if (Min != TestedLowerBound) + R.push_back(TestedLowerBound); + if (TestedLowerBound != TestedUpperBound) + R.push_back(TestedUpperBound); + if (TestedUpperBound != Max) + R.push_back(Max); + return true; } -static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *E) { - Expr *orig = E; - unsigned diagID = diag::err_uncasted_use_of_unknown_any; - while (true) { - E = E->IgnoreParenImpCasts(); - if (CallExpr *call = dyn_cast(E)) { - E = call->getCallee(); - diagID = diag::err_uncasted_call_of_unknown_any; +ExprResult BoundsCheckBuilder::Build(Sema &S, Expr *GuardedValue) { + // first, expand base pointer + Expr *BasePtr, *UpperBound, *LowerBound; + if (auto *Wide = getWidePointer()) { + BasePtr = OpaqueWrap(S, Wide); + if (Wide->isNullPointerConstantIgnoreCastsAndOVEs( + S.Context, Expr::NPC_NeverValueDependent) != Expr::NPCK_NotNull) { + UpperBound = BasePtr; + LowerBound = BasePtr; } else { - break; + ExprResult Upper = S.BuildUpperBoundExpr(BasePtr, BasePtr->getExprLoc()); + if (!(UpperBound = Upper.get())) + return ExprError(); + ExprResult Lower = S.BuildLowerBoundExpr(BasePtr, BasePtr->getExprLoc()); + if (!(LowerBound = Lower.get())) + return ExprError(); } - } + } else { + auto Pair = getWidePointerBounds(); + if (Pair.first == nullptr || Pair.second == nullptr) + return ExprError(); - SourceLocation loc; - NamedDecl *d; - if (DeclRefExpr *ref = dyn_cast(E)) { - loc = ref->getLocation(); - d = ref->getDecl(); - } else if (MemberExpr *mem = dyn_cast(E)) { - loc = mem->getMemberLoc(); - d = mem->getMemberDecl(); - } else if (ObjCMessageExpr *msg = dyn_cast(E)) { - diagID = diag::err_uncasted_call_of_unknown_any; - loc = msg->getSelectorStartLoc(); - d = msg->getMethodDecl(); - if (!d) { - S.Diag(loc, diag::err_uncasted_send_to_unknown_any_method) - << static_cast(msg->isClassMessage()) << msg->getSelector() - << orig->getSourceRange(); + BasePtr = OpaqueWrap(S, Pair.first); + UpperBound = Pair.second; + LowerBound = Pair.first; + } + + bool IsIndexCheck = !AccessLowerBound && !AccessUpperBound; + SmallVector Bounds; + if (IsIndexCheck) { + // If access bounds are indices, we can generate an int range check instead + // of a pointer range check. Generating an int range check has vastly + // superior codegen outcomes: since -fbounds-safety pointer arithmetic does not + // use inbounds GEPs, clang is unable to reduce an expression like + // `ptr[A] < ptr[B]` to `A < B` (as the non-inbounds GEP carries a risk of + // arithmetic overflow). Directly expressing `A < B` works around this + // issue. + // A full check would usually be + // `Lower <= AccessLower <= AccessUpper <= Upper`, + // where Lower/Upper are the accessed pointer's lower and upper bounds, and + // AccessLower/AccessUpper are the region that needs to be accessible. + // For instance, ptr[5] has: + // * Lower = ptr + 0 + // * AccessLower = ptr + 5 + // * AccessUpper = ptr + 6 + // * Upper = upper_bound(ptr) + // Translating to indices, we get something like + // `0 <= 5 <= 6 <= upper_bound(ptr) - &ptr[0]`. + // This optimizes very nicely to `6 <= (upper-&ptr[0])`. GEPs that calculate + // the upper bound and the lower bound of a pointer are inherently inbounds, + // so arithmetic on them isn't penalized, so the whole thing is about as + // good as we could make it. This is better even though it requires us to do + // a full bounds check on `ptr` (lower(ptr) <= ptr <= upper(ptr)) before we + // can do index-based comparisons. + // XXX: picking AST patterns for their codegen-ability is crummy. :( + if (!BuildIndexBounds(S, BasePtr, UpperBound, Bounds)) return ExprError(); - } } else { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) - << E->getSourceRange(); + Expr *MinExpr = BasePtr; + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_EndedByLowerBound)) { + MinExpr = LowerBound; + } + if (!BuildPtrBounds(S, MinExpr, UpperBound, Bounds)) + return ExprError(); + } + + SourceLocation Loc = GuardedValue->getExprLoc(); + ExprResult Cond = BuildLEChecks(S, Loc, Bounds, OVEs); + if (Cond.isInvalid()) return ExprError(); + + if (isOrNull()) { + ExprResult NullCheck = S.CreateBuiltinUnaryOp(Loc, UO_LNot, BasePtr); + if (NullCheck.isInvalid()) + return ExprError(); + // !Ptr || 0 <= Count <= (Upper - Ptr) + Cond = S.CreateBuiltinBinOp(Loc, BO_LOr, NullCheck.get(), Cond.get()); + if (Cond.isInvalid()) + return ExprError(); } - S.Diag(loc, diagID) << d << orig->getSourceRange(); + if (IsIndexCheck) { + // Index checks do the blissful assumption that lower <= ptr <= upper to + // use numbers in comparisons, but in fact, both of these conditions can be + // false, so they need to be checked separately. + Expr *Bounds[] = {LowerBound, BasePtr, UpperBound}; + // Check that pointer argument being passed is in range + // Lower <= Ptr <= Upper + ExprResult BasePtrBoundsCheck = BuildLEChecks(S, Loc, Bounds, OVEs); + if (!BasePtrBoundsCheck.get()) + return ExprError(); + Cond = S.CreateBuiltinBinOp( + Loc, BO_LAnd, BasePtrBoundsCheck.get(), Cond.get()); + if (!Cond.get()) + return ExprError(); + } - // Never recoverable. - return ExprError(); + return S.BuildBoundsCheckExpr(GuardedValue, Cond.get(), {}); } -ExprResult Sema::CheckPlaceholderExpr(Expr *E) { - if (!Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes on either side of a binop because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - ExprResult Result = CorrectDelayedTyposInExpr(E); - if (!Result.isUsable()) return ExprError(); - E = Result.get(); - } +ExprResult BoundsCheckBuilder::BuildLEChecks( + Sema &S, SourceLocation Loc, ArrayRef Bounds, + SmallVectorImpl &OVEs) { + assert(Bounds.size() >= 2); + Expr *CondAccum = nullptr; + Expr *Next = Bounds[Bounds.size()-1]; - const BuiltinType *placeholderType = E->getType()->getAsPlaceholderType(); - if (!placeholderType) return E; + // Reverse it to check the upper bound first. Upper bounds of `__counted_by` + // pointers are calculated using gep `inbounds`; Checking such `inbounds` + // pointer arithmetic first will help to optimize the same pointer arithmetic + // that doesn't have `inbounds`. + for (unsigned i = Bounds.size() - 1; i--;) { + Expr *Bound = Bounds[i]->IgnoreImpCasts(); + bool IsOpaqueValue = isa(Bound); - switch (placeholderType->getKind()) { - case BuiltinType::UnresolvedTemplate: { - auto *ULE = cast(E); - const DeclarationNameInfo &NameInfo = ULE->getNameInfo(); - // There's only one FoundDecl for UnresolvedTemplate type. See - // BuildTemplateIdExpr. - NamedDecl *Temp = *ULE->decls_begin(); - const bool IsTypeAliasTemplateDecl = isa(Temp); + // Make sure that comparison types are compatible before we wrap expressions + // in opaque values. + ExprResult NextResult = Next; + ExprResult BoundResult = Bound; + auto CmpTy = S.CheckCompareOperands(BoundResult, NextResult, Loc, BO_LE); + if (CmpTy.isNull() || !(Next = NextResult.get()) + || !(Bound = BoundResult.get())) + return ExprError(); - if (NestedNameSpecifierLoc Loc = ULE->getQualifierLoc(); Loc.hasQualifier()) - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << Loc.getNestedNameSpecifier() << NameInfo.getName().getAsString() - << Loc.getSourceRange() << IsTypeAliasTemplateDecl; - else - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << "" << NameInfo.getName().getAsString() << ULE->getSourceRange() - << IsTypeAliasTemplateDecl; - Diag(Temp->getLocation(), diag::note_referenced_type_template) - << IsTypeAliasTemplateDecl; + if (i != 0 && !IsOpaqueValue) { + // Bound will be reused in 'Prev <= Bound && Bound <= Next' thus materialize + // it. If the value was originally an opaque value, it doesn't need to be + // wrapped in a new one just because of implicit casts. + OVEs.push_back(OpaqueValueExpr::Wrap(S.Context, Bound)); + Bound = OVEs.back(); + } - return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}); + ExprResult LEBounds = S.CreateBuiltinBinOp(Loc, BO_LE, Bound, Next); + if (LEBounds.isInvalid()) + return ExprError(); + + if (!CondAccum) { + CondAccum = LEBounds.get(); + } else { + ExprResult CondAnd = S.CreateBuiltinBinOp(Loc, BO_LAnd, + CondAccum, LEBounds.get()); + if (CondAnd.isInvalid()) + return ExprError(); + CondAccum = CondAnd.get(); + } + + // Make sure an implicit cast is not reused in a different node. This + // may result in an unexpected expression type change since implicit + // casts are allowed to change their result types during Sema. + Next = Bound->IgnoreImpCasts(); } - // Overloaded expressions. - case BuiltinType::Overload: { - // Try to resolve a single function template specialization. - // This is obligatory. - ExprResult Result = E; - if (ResolveAndFixSingleFunctionTemplateSpecialization(Result, false)) - return Result; + return CondAccum; +} - // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization - // leaves Result unchanged on failure. - Result = E; - if (resolveAndFixAddressOfSingleOverloadCandidate(Result)) - return Result; +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + CopyExpr *DeclReplacer) { + SmallVector OVEs; + auto *OpaqueRoot = OpaqueValueExpr::EnsureWrapped(S.Context, FAMPtr, OVEs); + Expr *Result = CheckFlexibleArrayMemberSizeImpl(S, Loc, BCK, OpaqueRoot, OVEs, + OpaqueRoot, DeclReplacer) + .get(); + if (!Result) + return ExprError(); - // If that failed, try to recover with a call. - tryToRecoverWithCall(Result, PDiag(diag::err_ovl_unresolvable), - /*complain*/ true); - return Result; - } + // Materialize OpaqueValues + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); - // Bound member functions. - case BuiltinType::BoundMember: { - ExprResult result = E; - const Expr *BME = E->IgnoreParens(); - PartialDiagnostic PD = PDiag(diag::err_bound_member_function); - // Try to give a nicer diagnostic if it is a bound member that we recognize. - if (isa(BME)) { - PD = PDiag(diag::err_dtor_expr_without_call) << /*pseudo-destructor*/ 1; - } else if (const auto *ME = dyn_cast(BME)) { - if (ME->getMemberNameInfo().getName().getNameKind() == - DeclarationName::CXXDestructorName) - PD = PDiag(diag::err_dtor_expr_without_call) << /*destructor*/ 0; - } - tryToRecoverWithCall(result, PD, - /*complain*/ true); - return result; + // Unbind everything. + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); } + return Result; +} - // ARC unbridged casts. - case BuiltinType::ARCUnbridgedCast: { - Expr *realCast = ObjC().stripARCUnbridgedCast(E); - ObjC().diagnoseARCUnbridgedCast(realCast); - return realCast; +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSizeWithOVEs( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, CopyExpr *DeclReplacer) { + return CheckFlexibleArrayMemberSizeImpl(S, Loc, BCK, FAMPtr, OVEs, FAMPtr, + DeclReplacer); +} +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSizeImpl( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, Expr *GuardedValue, + CopyExpr *DeclReplacer) { + + FlexibleArrayMemberUtils FlexUtils(S); + + // The base address must be opaque since it will be used in multiple places. + auto *OpaqueRoot = dyn_cast(FAMPtr); + if (!OpaqueRoot) { + OVEs.push_back(OpaqueValueExpr::Wrap(S.Context, FAMPtr)); + OpaqueRoot = OVEs.back(); } - // Expressions of unknown type. - case BuiltinType::UnknownAny: - return diagnoseUnknownAnyExpr(*this, E); + if (!GuardedValue) + GuardedValue = OpaqueRoot; - // Pseudo-objects. - case BuiltinType::PseudoObject: - return PseudoObject().checkRValue(E); + SmallVector PathToFlex; + ArrayRef CountDecls; + auto *RT = FAMPtr->getType()->getPointeeType()->getAs(); + bool Found = FlexUtils.Find(RT->getDecl(), PathToFlex, CountDecls); + assert(Found); (void)Found; - case BuiltinType::BuiltinFn: { - // Accept __noop without parens by implicitly converting it to a call expr. - auto *DRE = dyn_cast(E->IgnoreParenImpCasts()); - if (DRE) { - auto *FD = cast(DRE->getDecl()); - unsigned BuiltinID = FD->getBuiltinID(); - if (BuiltinID == Builtin::BI__noop) { - E = ImpCastExprToType(E, Context.getPointerType(FD->getType()), - CK_BuiltinFnToFnPtr) - .get(); - return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy, - VK_PRValue, SourceLocation(), - FPOptionsOverride()); - } + Expr *FlexibleObj = FlexUtils.SelectFlexibleObject(PathToFlex, OpaqueRoot); - if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) { - // Any use of these other than a direct call is ill-formed as of C++20, - // because they are not addressable functions. In earlier language - // modes, warn and force an instantiation of the real body. - Diag(E->getBeginLoc(), - getLangOpts().CPlusPlus20 - ? diag::err_use_of_unaddressable_function - : diag::warn_cxx20_compat_use_of_unaddressable_function); - if (FD->isImplicitlyInstantiable()) { - // Require a definition here because a normal attempt at - // instantiation for a builtin will be ignored, and we won't try - // again later. We assume that the definition of the template - // precedes this use. - InstantiateFunctionDefinition(E->getBeginLoc(), FD, - /*Recursive=*/false, - /*DefinitionRequired=*/true, - /*AtEndOfTU=*/false); - } - // Produce a properly-typed reference to the function. - CXXScopeSpec SS; - SS.Adopt(DRE->getQualifierLoc()); - TemplateArgumentListInfo TemplateArgs; - DRE->copyTemplateArgumentsInto(TemplateArgs); - return BuildDeclRefExpr( - FD, FD->getType(), VK_LValue, DRE->getNameInfo(), - DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(), - DRE->getTemplateKeywordLoc(), - DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr); - } - } + // Create count expression + FieldDecl *FlexibleField = PathToFlex.back(); + ExprResult CountExpr = FlexUtils.BuildCountExpr( + FlexibleField, CountDecls, FlexibleObj, OVEs, DeclReplacer); + if (!CountExpr.get()) + return ExprError(); - Diag(E->getBeginLoc(), diag::err_builtin_fn_use); + // Create array base address + QualType FlexType = PathToFlex.back()->getType(); + Expr *ArrayBase = MemberExpr::CreateImplicit( + S.Context, FlexibleObj, FlexibleObj->getType()->isPointerType(), + PathToFlex.back(), FlexType, VK_LValue, OK_Ordinary); + // Ensure that the pointer isn't __bidi_indexable, as that would be + // problematic. + QualType DecayedTy = S.Context.getArrayDecayedType(FlexType); + DecayedTy = S.Context.getBoundsSafetyPointerType(DecayedTy, {}); + ExprResult DecayedArrayBase = S.ImpCastExprToType( + ArrayBase, DecayedTy, CK_ArrayToPointerDecay, VK_PRValue); + if (!DecayedArrayBase.get()) return ExprError(); - } - case BuiltinType::IncompleteMatrixIdx: - Diag(cast(E->IgnoreParens()) - ->getRowIdx() - ->getBeginLoc(), - diag::err_matrix_incomplete_index); + // ArrayToPointerDecay overrides the pointer attributes to be bidi_indexable, + // force it to be unspecified. + DecayedArrayBase.get()->setType(DecayedTy); + + SmallVector CheckArgs{OpaqueRoot, DecayedArrayBase.get(), + CountExpr.get()}; + + // Create bounds check expression. + return PredefinedBoundsCheckExpr::Create(S.Context, GuardedValue, BCK, + CheckArgs); +} + +ExprResult Sema::BuildTerminatedByToIndexableExpr(Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + // TODO: Add C++ support. + assert(!PointerExpr->getType()->isDependentType() && + "BoundsSafety does not support C++ yet"); + + ExprResult RValue = DefaultFunctionArrayLvalueConversion(PointerExpr); + if (RValue.isInvalid()) return ExprError(); + PointerExpr = RValue.get(); - // Expressions of unknown type. - case BuiltinType::ArraySection: - Diag(E->getBeginLoc(), diag::err_array_section_use) - << cast(E)->isOMPArraySection(); + QualType PtrTy = PointerExpr->getType(); + const auto *PtrVTT = PtrTy->getAs(); + + if (!PtrVTT) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_to_indexable_wrong_ptr_type) + << PtrTy; return ExprError(); + } - // Expressions of unknown type. - case BuiltinType::OMPArrayShaping: - return ExprError(Diag(E->getBeginLoc(), diag::err_omp_array_shaping_use)); + // __terminated_by() applies only to arrays and __single pointers, but since + // we decay arrays, we expect a __single pointer here. + assert(PtrTy->isSinglePointerType()); - case BuiltinType::OMPIterator: - return ExprError(Diag(E->getBeginLoc(), diag::err_omp_iterator_use)); + if (TerminatorExpr && TerminatorExpr != PtrVTT->getTerminatorExpr()) { + // The terminator must be an ICE. + std::optional TermVal = + TerminatorExpr->getIntegerConstantExpr(Context); + if (!TermVal.has_value()) { + Diag(TerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_terminator_must_be_const); + return ExprError(); + } - // Everything else should be impossible. -#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ - case BuiltinType::Id: -#include "clang/Basic/OpenCLImageTypes.def" -#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ - case BuiltinType::Id: -#include "clang/Basic/OpenCLExtensionTypes.def" -#define SVE_TYPE(Name, Id, SingletonId) \ - case BuiltinType::Id: -#include "clang/Basic/AArch64SVEACLETypes.def" -#define PPC_VECTOR_TYPE(Name, Id, Size) \ - case BuiltinType::Id: -#include "clang/Basic/PPCTypes.def" -#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/RISCVVTypes.def" -#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/WebAssemblyReferenceTypes.def" -#define AMDGPU_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/AMDGPUTypes.def" -#define BUILTIN_TYPE(Id, SingletonId) case BuiltinType::Id: -#define PLACEHOLDER_TYPE(Id, SingletonId) -#include "clang/AST/BuiltinTypes.def" - break; + llvm::APSInt PtrTermVal = PtrVTT->getTerminatorValue(Context); + if (!llvm::APSInt::isSameValue(*TermVal, PtrTermVal)) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_to_indexable_wrong_terminator) + << TerminatorExpr << PtrVTT->getTerminatorExpr(); + return ExprError(); + } } - llvm_unreachable("invalid placeholder type!"); -} + QualType RetTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::indexable()); + return new (Context) + TerminatedByToIndexableExpr(BuiltinLoc, RParenLoc, PointerExpr, + TerminatorExpr, IncludeTerminator, RetTy); +} + +ExprResult Sema::ActOnTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildTerminatedByToIndexableExpr(PointerExpr, TerminatorExpr, + /*IncludeTerminator=*/false, + BuiltinLoc, RParenLoc); +} + +ExprResult Sema::ActOnUnsafeTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildTerminatedByToIndexableExpr(PointerExpr, TerminatorExpr, + /*IncludeTerminator=*/true, + BuiltinLoc, RParenLoc); +} + +ExprResult Sema::BuildTerminatedByFromIndexableExpr( + Expr *TerminatorExpr, Expr *PointerExpr, Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc) { + // TODO: Add C++ support. + assert(!PointerExpr->getType()->isDependentType() && + "BoundsSafety does not support C++ yet"); + + // The terminator must be an ICE. + if (!TerminatorExpr->getIntegerConstantExpr(Context).has_value()) { + Diag(TerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_terminator_must_be_const); + return ExprError(); + } -bool Sema::CheckCaseExpression(Expr *E) { - if (E->isTypeDependent()) - return true; - if (E->isValueDependent() || E->isIntegerConstantExpr(Context)) - return E->getType()->isIntegralOrEnumerationType(); - return false; -} + ExprResult RValue = DefaultFunctionArrayLvalueConversion(PointerExpr); + if (RValue.isInvalid()) + return ExprError(); + PointerExpr = RValue.get(); -ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, - ArrayRef SubExprs, QualType T) { - if (!Context.getLangOpts().RecoveryAST) + QualType PtrTy = PointerExpr->getType(); + if (!PtrTy->isSafePointerType()) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_wrong_ptr_type) + << PtrTy; return ExprError(); + } - if (isSFINAEContext()) + // The pointee must be an integer or a thin pointer. + QualType PointeeTy = PtrTy->getPointeeType(); + if (!(PointeeTy->isIntegralOrEnumerationType() || + (PointeeTy->isPointerType() && + !PointeeTy->isPointerTypeWithBounds()))) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_wrong_pointee_type); return ExprError(); + } - if (T.isNull() || T->isUndeducedType() || - !Context.getLangOpts().RecoveryASTType) - // We don't know the concrete type, fallback to dependent type. - T = Context.DependentTy; + if (PointerToTerminatorExpr) { + ExprResult RValue = + DefaultFunctionArrayLvalueConversion(PointerToTerminatorExpr); + if (RValue.isInvalid()) + return ExprError(); + PointerToTerminatorExpr = RValue.get(); + + QualType PtrToTermTy = PointerToTerminatorExpr->getType(); + if (!PtrToTermTy->isPointerType()) { + Diag( + PointerToTerminatorExpr->getBeginLoc(), + diag:: + err_bounds_safety_terminated_by_from_indexable_wrong_ptr_to_term_type) + << PointerToTerminatorExpr->getType(); + return ExprError(); + } - return RecoveryExpr::Create(Context, T, Begin, End, SubExprs); + if (!Context.hasSameUnqualifiedType(PtrToTermTy->getPointeeType(), + PointeeTy)) { + Diag(PointerToTerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_pointee_mismatch); + return ExprError(); + } + } + + ExprResult TermCastRes(TerminatorExpr); + CastKind Kind = PrepareScalarCast(TermCastRes, PointeeTy); + TermCastRes = ImpCastExprToType(TerminatorExpr, PointeeTy, Kind); + if (!TermCastRes.get()) + return ExprError(); + TerminatorExpr = TermCastRes.get(); + + if (!PtrTy->isIndexablePointerType()) { + PtrTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::indexable()); + ExprResult PtrCastRes = + ImpCastExprToType(PointerExpr, PtrTy, CK_BoundsSafetyPointerCast); + if (!PtrCastRes.get()) + return ExprError(); + PointerExpr = PtrCastRes.get(); + } + + QualType SinglePtrTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::single()); + QualType RetTy = Context.getValueTerminatedType(SinglePtrTy, TerminatorExpr); + return new (Context) TerminatedByFromIndexableExpr( + BuiltinLoc, RParenLoc, PointerExpr, PointerToTerminatorExpr, RetTy); +} + +ExprResult Sema::ActOnUnsafeTerminatedByFromIndexable( + Expr *TerminatorExpr, Expr *PointerExpr, Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc) { + return BuildTerminatedByFromIndexableExpr(TerminatorExpr, PointerExpr, + PointerToTerminatorExpr, BuiltinLoc, + RParenLoc); } +/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b9e7f6861e116..9aee689b2ffcd 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6901,6 +6901,18 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, return QualType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (Context.hasSameType(LTy, RTy) && Context.getLangOpts().BoundsSafety) { + auto MergeResult = Context.canMergeTypeBounds(LTy, RTy); + if (MergeResult != ASTContext::BSPTMK_CanMerge) + Diag(QuestionLoc, + diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LTy << RTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange() << MergeResult; + return QualType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Neither is void. if (IsVectorConditional) return CheckVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 90e32a11c28b6..0e1c009c67498 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -23,6 +23,8 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" using namespace clang; using namespace sema; @@ -1276,7 +1278,13 @@ static bool isPointerToRecordType(QualType T) { ExprResult Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) { if (IsArrow && !Base->getType()->isFunctionType()) - return DefaultFunctionArrayLvalueConversion(Base); + return DefaultFunctionArrayLvalueConversion( + Base, + /* TO_UPSTREAM(BoundsSafety) ON */ + /*Diagnose*/ true, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion*/ true, + /*DisableFlexibleArrayPromotion*/ true); + /* TO_UPSTREAM(BoundsSafety) OFF */ return CheckPlaceholderExpr(Base); } @@ -1827,6 +1835,60 @@ void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { } } +/* TO_UPSTREAM(BoundsSafety) ON */ +/// BoundsSafety: When dynamic count pointer type is created for FieldDecl, +/// the argument expression is created as DeclRefExpr of the FieldDecl. +/// +/// \code +/// struct S { int *__counted_by(len+1) ptr; int len; }; +/// struct S s; +/// \endcode +/// +/// After a struct is instantiated, the argument expression of __counted_by() +/// should also be instantiated so that the expression reference the field +/// as MemberExpr instead of DeclRefExpr of FieldDecl. After transformation, +/// The type of 's.ptr' will be represented as 'int *__counted_by(s.len+1)'. +class TransformDeclRefField : public TreeTransform { + typedef TreeTransform BaseTransform; + Expr *BaseExpr; + bool IsArrow; + ExprValueKind VK; + +public: + TransformDeclRefField(Sema &SemaRef, Expr *BaseExpr, bool IsArrow) + : BaseTransform(SemaRef), BaseExpr(BaseExpr), IsArrow(IsArrow) { + if (!IsArrow) { + if (BaseExpr->getObjectKind() == OK_Ordinary) + VK = BaseExpr->getValueKind(); + else + VK = VK_PRValue; + } else { + VK = VK_LValue; + } + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + FieldDecl *Field = dyn_cast(E->getDecl()); + if (!Field) + return Owned(E); + + ExprValueKind FieldVK = Field->getType()->isReferenceType() ? VK_LValue : VK; + // Using RebuildMemberExpr in this context can cause recursion. + return MemberExpr::CreateImplicit(SemaRef.Context, BaseExpr, IsArrow, Field, Field->getType(), + FieldVK, OK_Ordinary); + } +}; + +ExprResult Sema::InstantiateDeclRefField(Expr *BaseExpr, bool IsArrow, + Expr *FieldRef) { + // FieldRef can be a MemberExpr, but TransformDeclRefField will recurse + // until it reaches the final MemberExpr where the base is a DeclRef of a + // FieldDecl, and transform that. + return TransformDeclRefField(*this, BaseExpr, IsArrow) + .TransformExpr(FieldRef); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprResult Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, SourceLocation OpLoc, const CXXScopeSpec &SS, diff --git a/clang/lib/Sema/SemaFixItUtils.cpp b/clang/lib/Sema/SemaFixItUtils.cpp index 2c85a53194301..52c2927f5fc2a 100644 --- a/clang/lib/Sema/SemaFixItUtils.cpp +++ b/clang/lib/Sema/SemaFixItUtils.cpp @@ -223,3 +223,107 @@ std::string Sema::getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const { return getScalarZeroExpressionForType(*T, Loc, *this); } + +/* TO_UPSTREAM(BoundsSafety) ON */ +std::tuple +BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(const TypeLoc TL, Sema &S) { + SourceLocation FixitLoc; + if (!TL) { + return std::make_tuple(SourceLocation(), false); + } + + // The default is false because we don't want to add spaces unnecessarily. + // E.g. + // + // foo(int*) + // + // should be transformed to + // + // foo(int* __attribute) + // + // not + // + // foo(int* __attribute ) + // + // The later would likely break the style guide of existing projects + bool SpaceNeededAfterAttribute = false; + if (auto PTL = TL.getAs()) { + SourceLocation StarLoc = PTL.getStarLoc(); + if (StarLoc.isInvalid()) { + return std::make_tuple(StarLoc, false); + } + + if (auto TK = Lexer::findNextToken(StarLoc, S.SourceMgr, S.getLangOpts())) { + FixitLoc = TK->getLocation(); + // For identifiers that follow the type we need to do + // foo(int * __attribute param) + // not + // foo(int * __attributeparam) + // + // FIXME: Explain the macro check + if (StarLoc.isMacroID() || TK->isAnyIdentifier()) { + SpaceNeededAfterAttribute = true; + } + } + } else { + // a typedef or somesuch + FixitLoc = Lexer::findNextTokenLocationAfterTokenAt( + TL.getEndLoc(), S.SourceMgr, S.getLangOpts()); + SpaceNeededAfterAttribute = true; // Correct? + } + return std::make_tuple(FixitLoc, SpaceNeededAfterAttribute); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S) { + return CreateAnnotateVarDeclOrFieldDeclFixIt(VD, Attribute, S); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + const FieldDecl *FD, const llvm::StringRef Attribute, Sema &S) { + return CreateAnnotateVarDeclOrFieldDeclFixIt(FD, Attribute, S); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotateVarDeclOrFieldDeclFixIt( + const DeclaratorDecl *DD, const llvm::StringRef Attribute, Sema &S) { + assert(isa(DD) || isa(DD)); + auto AssignedVarTL = DD->getTypeSourceInfo()->getTypeLoc(); + SourceLocation InsertionPoint; + bool NeedsSpace = false; + +#ifndef NDEBUG + // The current implementation assumes that there isn't an existing attribute + // on the VarDecl/FieldDecl. This restriction could be lifted in the future. + // It requires that the fixit delete the old attribute. rdar://114478465 + auto DeclTy = DD->getType(); + assert(DeclTy->isPointerType()); + assert(DeclTy->hasAttr(attr::PtrAutoAttr)); +#endif + + std::tie(InsertionPoint, NeedsSpace) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(AssignedVarTL, S); + + if (InsertionPoint.isInvalid()) + return FixItHint(); + + std::string AttrStr(Attribute); + if (NeedsSpace) + AttrStr += " "; + return FixItHint::CreateInsertion(InsertionPoint, AttrStr); +} + +void BoundsSafetyFixItUtils::CreateAnnotateAllPointerDeclsFixIts( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S, + llvm::SmallVectorImpl> + &FixIts) { + + const VarDecl *CurrentDecl = VD; + while (CurrentDecl) { + auto FixIt = CreateAnnotatePointerDeclFixIt(CurrentDecl, Attribute, S); + if (!FixIt.isNull()) + FixIts.emplace_back(std::make_tuple( + FixIt, static_cast(CurrentDecl))); + CurrentDecl = CurrentDecl->getPreviousDecl(); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ \ No newline at end of file diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp index 2f061b6c50293..83bc9f1505122 100644 --- a/clang/lib/Sema/SemaFunctionEffects.cpp +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -805,7 +805,8 @@ class Analyzer { auto MaybeAddTemplateNote = [&](const Decl *D) { if (const FunctionDecl *FD = dyn_cast(D)) { - while (FD != nullptr && FD->isTemplateInstantiation()) { + while (FD != nullptr && FD->isTemplateInstantiation() && + FD->getPointOfInstantiation().isValid()) { S.Diag(FD->getPointOfInstantiation(), diag::note_func_effect_from_template); FD = FD->getTemplateInstantiationPattern(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index eea4bdfa68b52..a83002ddacaf4 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" #include "CheckExprLifetime.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" @@ -17,6 +18,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/IgnoreExpr.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/CharInfo.h" @@ -46,6 +48,215 @@ using namespace clang; // Sema Initialization Checking //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON*/ +class FindDeclRefs : public RecursiveASTVisitor { + SmallVectorImpl &Decls; +public: + FindDeclRefs(SmallVectorImpl &Decls) : Decls(Decls) {} + + bool TraverseDeclRefExpr(DeclRefExpr *D) { + Decls.push_back(D->getDecl()); + return false; + } +}; + +// Check if value-terminated types are correctly initialized. InitListChecker +// cannot be used, as it doesn't traverse all nested members. +static void checkValueTerminatedInit(Sema &S, SourceLocation Loc, + const llvm::Twine &Name, QualType T, + Expr *Init, bool ZeroInit) { + const auto *VTT = T->getAs(); + + // We shouldn't have both Init and ZeroInit flag set. + assert(!Init || !ZeroInit); + + if (Init) { + Init = Init->IgnoreParenImpCasts(); + + // Check if this is copy init from a variable with the same type. For + // pointers, __terminated_by() attributes are checked in + // CheckValueTerminatedAssignmentConstraints. + // TODO: We handle here only the basic case (rdar://103147634). + if (const auto *DRE = dyn_cast(Init)) + if (S.getASTContext().hasSameUnqualifiedType(DRE->getType(), T)) + return; + + if (auto *CLE = dyn_cast(Init)) + Init = CLE->getInitializer(); + } + + auto getInitAt = [&](unsigned Index) -> std::pair { + if (Init) { + if (isa(Init)) + return {nullptr, true}; + if (auto *ILE = dyn_cast(Init)) { + if (Index < ILE->getNumInits()) + return {ILE->getInit(Index), ZeroInit}; + return {nullptr, true}; + } + } + return {nullptr, ZeroInit}; + }; + + // Recursively check each field. + if (const auto *RT = T->getAs()) { + unsigned Index = 0; + for (const FieldDecl *FD : RT->getDecl()->fields()) { + Expr *I; + bool ZI; + std::tie(I, ZI) = getInitAt(Index); + checkValueTerminatedInit( + S, Loc, Name.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), I, ZI); + Index++; + } + return; + } + + // Recursively check each element of an array. + // If the array has a __terminated_by attribute, don't check the terminator + // at the end. The element at the end cannot be used anyway. + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + QualType ET = CAT->getElementType(); + unsigned End = CAT->getSize().getZExtValue(); + if (VTT && End > 0) + --End; + for (unsigned Index = 0; Index < End; ++Index) { + Expr *I; + bool ZI; + std::tie(I, ZI) = getInitAt(Index); + checkValueTerminatedInit(S, Loc, + Name.concat(llvm::Twine('[') + .concat(llvm::utostr(Index)) + .concat(llvm::Twine(']'))), + ET, I, ZI); + } + // fallthrough + } + + if (!VTT) + return; + + assert(T->isConstantArrayType() || T->isIncompleteArrayType() || + T->isPointerType()); + + // The constraints for __terminated_by() pointers are handled in + // CheckValueTerminatedAssignmentConstraints. + if (T->isPointerType()) + return; + + llvm::APSInt TermVal = VTT->getTerminatorValue(S.Context); + + llvm::APInt Size; + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + Size = CAT->getSize(); + assert(Size.isStrictlyPositive()); + } + + std::optional ActualVal; + Expr *GotExpr = nullptr; + const StringLiteral *GotSL = nullptr; + size_t GotSLIndex = 0; + + if (ZeroInit || Init) + ActualVal = llvm::APSInt(TermVal.getBitWidth(), TermVal.isUnsigned()); + + if (Init) { + if (isa(Init)) { + // Nothing to do. + } else if (auto *ILE = dyn_cast(Init)) { + unsigned NumInits = ILE->getNumInits(); + if (!Size && NumInits == 0) { + S.Diag(Loc, + diag::err_bounds_safety_terminated_by_incomplete_array_empty_init) + << Name.str(); + return; + } + if (Size.isZero() || Size.ule(NumInits)) { + unsigned Last = (!Size.isZero() ? Size.getZExtValue() : NumInits) - 1; + Init = ILE->getInit(Last); + bool Ok = Init->EvaluateAsTerminatorValue(*ActualVal, S.Context, + Expr::SE_AllowSideEffects); + if (!Ok) { + S.Diag( + Init->getBeginLoc(), + diag:: + err_bounds_safety_terminated_by_terminator_in_array_must_be_const) + << Name.str(); + return; + } + GotExpr = Init; + } + Loc = Init->getBeginLoc(); + } else if (const auto *SL = dyn_cast(Init)) { + unsigned Len = SL->getLength(); + if (!Size.isZero() && Size.ule(Len)) { + size_t Last = Size.getZExtValue() - 1; + uint32_t Unit = SL->getCodeUnit(Last); + llvm::APInt Val(TermVal.getBitWidth(), Unit, TermVal.isSigned()); + ActualVal = llvm::APSInt(Val, TermVal.isUnsigned()); + GotSL = SL; + GotSLIndex = Last; + } + Loc = SL->getBeginLoc(); + } else { + S.Diag(Init->getBeginLoc(), + diag::err_bounds_safety_terminated_by_wrong_initializer_kind) + << Name.str(); + return; + } + } + + if (!ActualVal) { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_uninitialized) << Name.str(); + return; + } + + if (*ActualVal != TermVal) { + if (GotExpr) { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() << GotExpr; + } else if (GotSL) { + std::string Str; + llvm::raw_string_ostream SS(Str); + CharacterLiteralKind Kind; + switch (GotSL->getKind()) { + case StringLiteralKind::Ordinary: + case StringLiteralKind::Unevaluated: + Kind = CharacterLiteralKind::Ascii; + break; + case StringLiteralKind::Wide: + Kind = CharacterLiteralKind::Wide; + break; + case StringLiteralKind::UTF8: + Kind = CharacterLiteralKind::UTF8; + break; + case StringLiteralKind::UTF16: + Kind = CharacterLiteralKind::UTF16; + break; + case StringLiteralKind::UTF32: + Kind = CharacterLiteralKind::UTF32; + break; + } + CharacterLiteral::print(GotSL->getCodeUnit(GotSLIndex), Kind, SS); + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() << SS.str(); + } else { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() + << toString(*ActualVal, 10, TermVal.isSigned()); + } + } +} + +void Sema::CheckValueTerminatedUninitialized(const VarDecl *VD) { + bool ZeroInit = VD->hasGlobalStorage(); + checkValueTerminatedInit(*this, VD->getLocation(), VD->getName(), + VD->getType(), + /*Init=*/nullptr, ZeroInit); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Check whether T is compatible with a wide character type (wchar_t, /// char16_t or char32_t). static bool IsWideCharCompatible(QualType T, ASTContext &Context) { @@ -501,6 +712,8 @@ class InitListChecker { bool CheckFlexibleArrayInit(const InitializedEntity &Entity, Expr *InitExpr, FieldDecl *Field, bool TopLevelObject); + // TO_UPSTREAM(BoundsSafety) + void CheckFlexibleArrayInitCount(InitListExpr *IList); void CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc); @@ -1040,6 +1253,8 @@ InitListChecker::InitListChecker( if (RequiresSecondPass && !hadError) FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass, nullptr, 0); + if (!VerifyOnly) + CheckFlexibleArrayInitCount(FullyStructuredList); } if (hadError && FullyStructuredList) FullyStructuredList->markError(); @@ -2235,6 +2450,96 @@ bool InitListChecker::CheckFlexibleArrayInit(const InitializedEntity &Entity, return FlexArrayDiag != diag::ext_flexible_array_init; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void InitListChecker::CheckFlexibleArrayInitCount(InitListExpr *IList) { + if (!SemaRef.getLangOpts().BoundsSafety) + return; + assert(!VerifyOnly); + assert(!IList->getType()->isVoidType()); + assert(IList->isSemanticForm()); + + auto *RD = getRecordDecl(IList->getType()); + if (!RD || !RD->hasFlexibleArrayMember()) + return; + + assert(IList->getNumInits() > 0); + Expr *InitExpr = IList->getInit(IList->getNumInits() - 1); + assert(!InitExpr->containsErrors()); + if (!InitExpr->getType()->isArrayType()) + return; + + FieldDecl *Field = nullptr; + for (FieldDecl *FD : RD->fields()) + Field = FD; + assert(Field); + assert(Field->getType()->isIncompleteArrayType()); + + SourceLocation BestSourceLoc = InitExpr->getBeginLoc(); + if (BestSourceLoc.isInvalid()) + BestSourceLoc = IList->getBeginLoc(); + bool Diag = false; + if (const auto *DCPTy = Field->getType()->getAs()) { + Expr *CountExpr = DCPTy->getCountExpr(); + // Build count expression, replacing field declarations with their + // initializer value, and then evaluate that. + CopyExpr Copier(SemaRef); + SmallVector Decls; + FindDeclRefs(Decls).TraverseStmt(CountExpr); + for (Decl *D : Decls) { + auto VD = cast(D); + Expr *Val = IList->getInitForField(VD); + assert(Val && !isa(Val)); + Copier.UnsafelyAddDeclSubstitution(VD, Val); + } + + ExprResult R = Copier.TransformExpr(CountExpr); + if (R.isInvalid()) + return; + Expr::EvalResult Res; + if (!R.get()->EvaluateAsRValue(Res, SemaRef.Context)) + return; // error will be emitted later about not being compile-time + // constant + + if (!Res.Val.isInt()) { + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_non_int_count_init) + << CountExpr << R.get() << R.get()->getType(); + Diag = true; + } else { + llvm::APSInt CountExprValue = Res.Val.getInt(); + + llvm::APSInt InitCount; + if (InitExpr) + if (auto *ArrayTy = + SemaRef.Context.getAsConstantArrayType(InitExpr->getType())) + InitCount = ArrayTy->getSize(); + + if (!llvm::APSInt::isSameValue(InitCount, CountExprValue)) { + std::string InitCountS; + std::string CountExprValueS; + { + llvm::raw_string_ostream ICS(InitCountS); + InitCount.print(ICS, InitCount.isSigned()); + llvm::raw_string_ostream CEVS(CountExprValueS); + CountExprValue.print(CEVS, CountExprValue.isSigned()); + } + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_wrong_count) + << InitCountS << CountExprValueS << (InitCount != 1); + Diag = true; + } + } + } else { + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_not_counted); + Diag = true; + } + if (Diag) + SemaRef.Diag(Field->getLocation(), diag::note_flexible_array_member) + << Field; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void InitListChecker::CheckStructUnionTypes( const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType, CXXRecordDecl::base_class_const_range Bases, RecordDecl::field_iterator Field, @@ -7997,6 +8302,10 @@ ExprResult InitializationSequence::Perform(Sema &S, else if ((*ResultType)->isLValueReferenceType()) Ty = S.Context.getLValueReferenceType(Ty, (*ResultType)->castAs()->isSpelledAsLValue()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *VTT = Step->Type->getAs()) + Ty = S.Context.getValueTerminatedType(Ty, VTT->getTerminatorExpr()); + /* TO_UPSTREAM(BoundsSafety) OFF*/ *ResultType = Ty; } @@ -8107,6 +8416,16 @@ ExprResult InitializationSequence::Perform(Sema &S, Kind.getRange().getEnd()); } else { CurInit = new (S.Context) ImplicitValueInitExpr(Step->Type); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Note the return value isn't used to return early + // to preserve the AST as best as possible even though an error + // might have occurred. For struct initialization it also allows + // all field assignments to be checked rather than bailing on the + // first error. + S.BoundsSafetyCheckInitialization(Entity, Kind, Sema::AA_Initializing, + /*LHSType=*/Step->Type, + /*RHSExpr=*/CurInit.get()); + /* TO_UPSTREAM(BoundsSafety) OFF*/ } break; } @@ -8125,6 +8444,60 @@ ExprResult InitializationSequence::Perform(Sema &S, return ExprError(); CurInit = Result; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + S.isCompatibleBoundsUnsafeAssignment(ConvTy) && + S.allowBoundsUnsafePointerAssignment(Entity.getType(), Result.get(), + Result.get()->getExprLoc())) { + ConvTy = Sema::Compatible; + } + if (S.getLangOpts().BoundsSafety && !Entity.isParameterKind()) { + if (!S.CheckDynamicBoundVariableEscape(Step->Type, Result.get())) + return ExprError(); + } + if (S.getLangOpts().BoundsSafety && + (Entity.isParameterKind() || + Entity.getKind() == InitializedEntity::EK_Result || + Entity.getKind() == InitializedEntity::EK_Member)) { + if (Step->Type->isSinglePointerType() && + !Step->Type->isBoundsAttributedType() && + (!SourceType->isSinglePointerType() || + SourceType->isBoundsAttributedType())) { + FlexibleArrayMemberUtils FlexUtils(S); + if (FlexUtils.GetFlexibleRecord(Step->Type->getPointeeType())) { + Expr *FAMPtr = CurInit.get(); + // Unwrap if it's OpaqueValueExpr since `isNullPointerConstant` + // doesn't look into the source expr of OpaqueValueExpr. + auto *PtrUnwrap = FAMPtr->IgnoreParenCasts(); + while (auto *PtrOVE = dyn_cast(PtrUnwrap)) { + PtrUnwrap = PtrOVE->getSourceExpr()->IgnoreParenCasts(); + } + + if (PtrUnwrap->isNullPointerConstant( + S.Context, Expr::NPC_NeverValueDependent) == + Expr::NPCK_NotNull) { + auto *CE = dyn_cast(FAMPtr); + // Ensure we pass an implicit bounds pointer. + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast) + FAMPtr = CE->getSubExpr(); + else + CE = nullptr; + ExprResult Ckd = BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + S, FAMPtr->getBeginLoc(), + BoundsCheckKind::FlexibleArrayCountCast, FAMPtr); + if (Ckd.isInvalid()) + return ExprError(); + + if (CE) { + CE->setSubExpr(Ckd.get()); + } else { + CurInit = Ckd; + } + } + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ // If this is a call, allow conversion to a transparent union. ExprResult CurInitExprRes = CurInit; if (ConvTy != Sema::Compatible && @@ -8154,11 +8527,33 @@ ExprResult InitializationSequence::Perform(Sema &S, } bool Complained; + ValueDecl *Assignee = nullptr; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety) { + // Currently the Assignee is only needed for bounds-safety diagnostics + // so guard passing this information along in this mode. + Assignee = Entity.getDecl(); + } + + // Note the return value isn't used to return early so that additional + // diagnostics can be emitted and to preserve the AST as best as possible + // even though an error might have occurred. For struct initialization it + // also allows all field assignments to be checked rather than bailing on + // the first error. + (void)S.BoundsSafetyCheckInitialization( + Entity, Kind, /*Action=*/getAssignmentAction(Entity, true), + /*LHSType=*/Step->Type, /*RHSExpr=*/InitialCurInit.get()); + + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Upstream doesn't have `Assignee`. if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(), Step->Type, SourceType, InitialCurInit.get(), getAssignmentAction(Entity, true), - &Complained)) { + &Complained, Assignee)) { + /* TO_UPSTREAM(BoundsSafety) OFF*/ PrintInitLocationNote(S, Entity); return ExprError(); } else if (Complained) @@ -8173,6 +8568,13 @@ ExprResult InitializationSequence::Perform(Sema &S, S.Context.getAsArrayType(Ty), S, S.getLangOpts().C23 && initializingConstexprVariable(Entity)); + /* TO_UPSTREAM(BoundsSafety) ON*/ + const auto *VTT = Step->Type->getAs(); + if (VTT && UpdateType) { + *ResultType = S.Context.getValueTerminatedType( + *ResultType, VTT->getTerminatorExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ break; } @@ -8223,6 +8625,12 @@ ExprResult InitializationSequence::Perform(Sema &S, *ResultType = S.Context.getConstantArrayType( IncompleteDest->getElementType(), ConstantSource->getSize(), ConstantSource->getSizeExpr(), ArraySizeModifier::Normal, 0); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *VTT = Step->Type->getAs()) { + *ResultType = S.Context.getValueTerminatedType( + *ResultType, VTT->getTerminatorExpr()); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ } } } @@ -8456,6 +8864,25 @@ ExprResult InitializationSequence::Perform(Sema &S, CheckMoveOnConstruction(S, Init, Entity.getKind() == InitializedEntity::EK_Result); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.LangOpts.BoundsSafety) { + auto *Init = CurInit.get(); + const auto K = Entity.getKind(); + if (Init && Entity.getParent() == nullptr && + (K == InitializedEntity::EK_Variable || + K == InitializedEntity::EK_Parameter || + K == InitializedEntity::EK_Result)) { + const ValueDecl *D = Entity.getDecl(); + StringRef Name = D ? D->getName() : StringRef(); + SourceLocation Loc = D && K == InitializedEntity::EK_Variable + ? D->getLocation() + : Init->getBeginLoc(); + checkValueTerminatedInit(S, Loc, Name, Entity.getType(), Init, + /*ZeroInit=*/false); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Init; } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index e67a179f9be7b..8ac5b3065fb4f 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1947,8 +1947,6 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); ActOnFinishFunctionBody(LSI.CallOperator, Body); - maybeAddDeclWithEffects(LSI.CallOperator); - return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); } @@ -2281,6 +2279,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: break; } + maybeAddDeclWithEffects(LSI->CallOperator); } return MaybeBindToTemporary(Lambda); diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index 32d42f3c3f3bb..ba3f82b30497b 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -240,6 +240,38 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, return SourceLocation(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Returns true if given expression has bounded pointer type that cannot be used as inline assembly argument; false otherwise. +static bool checkAsmArgumentsWithBounds(Sema &S, Expr *E) { + if (E->getType()->isBoundsAttributedType()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_ptr_with_bounds_in_assembly); + return true; + } else if (auto PT = E->getType()->getAs()) { + if (!PT->hasRawPointerLayout()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_ptr_with_bounds_in_assembly); + return true; + } + } + const Expr* const Base = E->IgnoreParenCasts(); + if (auto DRE = dyn_cast_or_null(Base)) { + if (auto ReferredVar = dyn_cast_or_null(DRE->getDecl())) { + if (ReferredVar->hasAttr()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_dyn_count_in_assembly); + return true; + } + } + } else if (auto DRE = dyn_cast_or_null(Base)) { + if (auto ReferredField = dyn_cast_or_null(DRE->getMemberDecl())) { + if (ReferredField->hasAttr()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_dyn_count_in_assembly); + return true; + } + } + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -307,6 +339,13 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, << OutputExpr->getType() << 0 /*Input*/ << OutputExpr->getSourceRange()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (checkAsmArgumentsWithBounds(*this, OutputExpr)) + return StmtError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + OutputConstraintInfos.push_back(Info); // If this is dependent, just continue. @@ -406,6 +445,22 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, if (Result.isInvalid()) return StmtError(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (checkAsmArgumentsWithBounds(*this, Result.get()->IgnoreImpCasts())) + return StmtError(); + } + // BoundsSafety: non-lvalue internal bounds pointers shall cast to a raw pointer + // to pass as the inline assembly input. + auto *PTy = Result.get()->getType()->getAs(); + if (PTy && !PTy->hasRawPointerLayout()) { + QualType RawPTy = Context.getPointerType(PTy->getPointeeType()); + Result = ImpCastExprToType(Result.get(), RawPTy, CK_BoundsSafetyPointerCast); + if (Result.isInvalid()) + return StmtError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + InputExpr = Exprs[i] = Result.get(); if (Info.requiresImmediateConstant() && !Info.allowsRegister()) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ca71542d886fa..4691da69de155 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2119,7 +2119,6 @@ DeclResult Sema::CheckClassTemplate( NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); - ProcessAPINotes(NewClass); if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b7b857ebf804b..a861ec206c705 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4188,6 +4188,19 @@ static bool AdjustFunctionParmAndArgTypesForDeduction( ArgType, S.Context.getDefaultOpenCLPointeeAddrSpace()); ArgType = S.Context.getLValueReferenceType(ArgType); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + } else if (S.getLangOpts().BoundsSafety) { + if (ArgType->isArrayType()) { + ArgType = S.Context.getArrayDecayedType(ArgType); + ArgType = S.Context.getBoundsSafetyPointerType( + ArgType, BoundsSafetyPointerAttributes::bidiIndexable()); + } else if (ArgType->isFunctionType()) { + ArgType = S.Context.getPointerType(ArgType, + BoundsSafetyPointerAttributes::single()); + } else { + ArgType = ArgType.getUnqualifiedType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } else { // C++ [temp.deduct.call]p2: // If P is not a reference type: @@ -5184,6 +5197,23 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, return DeductionFailed(TDK); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && + Init->getType()->isBoundsAttributedType()) { + unsigned ErrIdx; + QualType Ty = Init->getType(); + if (auto *DCPTy = Ty->getAs()) { + ErrIdx = DCPTy->getKind(); + } else { + auto *DRPTy = Ty->getAs(); + assert(DRPTy); + ErrIdx = DRPTy->getEndPointer() ? 4 : 5; + } + Diag(Loc, diag::err_bounds_safety_auto_dynamic_bound) << ErrIdx; + return DeductionFailed(TemplateDeductionResult::AlreadyDiagnosed); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Could be null if somehow 'auto' appears in a non-deduced context. if (Deduced[0].getKind() != TemplateArgument::Type) return DeductionFailed(TemplateDeductionResult::Incomplete); @@ -5197,7 +5227,13 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, } if (!Result.isNull()) { - if (!Context.hasSameType(DeducedType, Result)) { + if (!Context.hasSameType(DeducedType, Result) + /* TO_UPSTREAM(BoundsSafety) ON*/ + || (Context.getLangOpts().BoundsSafety && + Context.canMergeTypeBounds(DeducedType, Result) != + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) { Info.FirstArg = Result; Info.SecondArg = DeducedType; return DeductionFailed(TemplateDeductionResult::Inconsistent); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index a7939acef61f0..1036f55212849 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -20,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeLocVisitor.h" @@ -48,6 +50,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SaveAndRestore.h" #include #include @@ -180,6 +183,15 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_TypeNullableResult: \ case ParsedAttr::AT_TypeNullUnspecified +/* TO_UPSTREAM(BoundsSafety) ON */ +// BoundsSafety type qualifiers. +#define BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST \ + case ParsedAttr::AT_PtrBidiIndexable: \ + case ParsedAttr::AT_PtrSingle: \ + case ParsedAttr::AT_PtrIndexable: \ + case ParsedAttr::AT_PtrUnsafeIndexable +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { /// An object which stores processing state for the entire /// GetTypeForDeclarator process. @@ -277,7 +289,8 @@ namespace { /// Get an attributed type for the given attribute, and remember the Attr /// object so that we can attach it to the AttributedTypeLoc. - QualType getAttributedType(Attr *A, QualType ModifiedType, + // TO_UPSTREAM(BoundsSafety): In upstream Attr *A is not `const`. + QualType getAttributedType(const Attr *A, QualType ModifiedType, QualType EquivType) { QualType T = sema.Context.getAttributedType(A, ModifiedType, EquivType); @@ -332,6 +345,31 @@ namespace { llvm_unreachable("no Attr* for AttributedType*"); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Get the Attr* for a given attributed type. + const Attr *getAttrForAttributedType(const AttributedType *AT) { + if (!AttrsForTypesSorted) { + llvm::stable_sort(AttrsForTypes, llvm::less_first()); + AttrsForTypesSorted = true; + } + + // FIXME: This is quadratic if we have lots of reuses of the same + // attributed type. + for (auto It = std::partition_point( + AttrsForTypes.begin(), AttrsForTypes.end(), + [=](const TypeAttrPair &A) { return A.first < AT; }); + It != AttrsForTypes.end() && It->first == AT; ++It) { + if (It->second) { + return It->second; + } + } + + // If the AttributedType was created in another Decl it will have been + // removed already + return nullptr; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + SourceLocation getExpansionLocForMacroQualifiedType(const MacroQualifiedType *MQT) const { auto FoundLoc = LocsForMacros.find(MQT); @@ -727,6 +765,12 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, // Microsoft type attributes cannot go after the declarator-id. continue; + /* TO_UPSTREAM(BoundsSafety) ON*/ + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + // BoundsSafety pointer type attributes cannot go after the declarator-id. + continue; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + NULLABILITY_TYPE_ATTRS_CASELIST: // Nullability specifiers cannot go after the declarator-id. @@ -1781,7 +1825,8 @@ static QualType deduceOpenCLPointeeAddrSpace(Sema &S, QualType PointeeType) { return PointeeType; } -QualType Sema::BuildPointerType(QualType T, +// TO_UPSTREAM(BoundsSafety): Add `BoundsSafetyPointerAttributes A` +QualType Sema::BuildPointerType(QualType T, BoundsSafetyPointerAttributes A, SourceLocation Loc, DeclarationName Entity) { if (T->isReferenceType()) { // C++ 8.3.2p4: There shall be no ... pointers to references ... @@ -1830,7 +1875,8 @@ QualType Sema::BuildPointerType(QualType T, } // Build the pointer type. - return Context.getPointerType(T); + // TO_UPSTREAM(BoundsSafety): Pass `A` + return Context.getPointerType(T, A); } QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, @@ -2538,6 +2584,18 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { if (T.isVolatileQualified() && getLangOpts().CPlusPlus20) Diag(Loc, diag::warn_deprecated_volatile_return) << T; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety does not allow you to return a type with a flexible array member + // by value. + if (getLangOpts().BoundsSafety) { + auto *RecordTy = T->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + Diag(Loc, diag::err_flexible_array_member_passed_by_copy) << T; + return true; + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (T.getAddressSpace() != LangAS::Default && getLangOpts().HLSL) return true; return false; @@ -4661,8 +4719,13 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); } } - - T = S.BuildPointerType(T, DeclType.Loc, Name); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Declare and pass `BoundsSafetyPointerAttributes A`. + { + BoundsSafetyPointerAttributes A; + T = S.BuildPointerType(T, A, DeclType.Loc, Name); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ if (DeclType.Ptr.TypeQuals) T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); break; @@ -4991,6 +5054,24 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety does not allow you to return a type with a flexible array + // member by value. + if (S.getLangOpts().BoundsSafety) { + SourceLocation DiagLoc; + auto *RecordTy = T->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + if (TInfo) { + DiagLoc = TInfo->getTypeLoc().getBeginLoc(); + } else { + DiagLoc = D.getDeclSpec().getTypeSpecTypeLoc(); + } + S.Diag(DiagLoc, diag::err_flexible_array_member_passed_by_copy) << T; + D.setInvalidType(true); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // cv-qualifiers on return types are pointless except when the type is a // class type in C++. if ((T.getCVRQualifiers() || T->isAtomicType()) && @@ -6014,7 +6095,8 @@ namespace { TypeSourceInfo *TInfo = nullptr; Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); assert(TInfo); - TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); + // TO_UPSTREAM(BoundsSafety): initializeFullCopy -> copy + TL.getValueLoc().copy(TInfo->getTypeLoc()); } else { TL.setKWLoc(DS.getAtomicSpecLoc()); // No parens, to indicate this was spelled as an _Atomic qualifier. @@ -6061,6 +6143,12 @@ namespace { void VisitDecayedTypeLoc(DecayedTypeLoc TL) { llvm_unreachable("decayed type locs not expected here!"); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + llvm_unreachable("value terminated type locs not expected here!"); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void VisitArrayParameterTypeLoc(ArrayParameterTypeLoc TL) { llvm_unreachable("array parameter type locs not expected here!"); } @@ -6281,6 +6369,14 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, bool HasDesugaredTypeLoc = true; while (HasDesugaredTypeLoc) { switch (CurrTL.getTypeLocClass()) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + case TypeLoc::Atomic: { + auto TL = CurrTL.castAs(); + fillAtomicQualLoc(TL, D.getTypeObject(i)); + CurrTL = TL.getValueLoc().getUnqualifiedLoc(); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ case TypeLoc::MacroQualified: { auto TL = CurrTL.castAs(); TL.setExpansionLoc( @@ -6297,6 +6393,9 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, } case TypeLoc::Adjusted: + /* TO_UPSTREAM(BoundsSafety) ON*/ + case TypeLoc::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF*/ case TypeLoc::BTFTagAttributed: { CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); break; @@ -8288,6 +8387,13 @@ static void HandlePtrAuthQualifier(QualType &type, const ParsedAttr &attr, return; } + /* TO_UPSTREAM(BoundsSafety) ON */ + if (type->isIndexablePointerType() || type->isBidiIndexablePointerType()) { + attr.setInvalid(); + S.Diag(attr.getLoc(), diag::err_bounds_safety_ptrauth_on_indexable_pointer); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + Expr *keyArg = attr.getArgAsExpr(0); Expr *isAddressDiscriminatedArg = @@ -8522,6 +8628,797 @@ static void HandleRISCVRVVVectorBitsTypeAttr(QualType &CurType, CurType = S.Context.getVectorType(EltType, NumElts, VecKind); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// @brief __single, __bidi_indexable and friends are macros declared in +/// ptrcheck.h. Treat them as if they were the actual token to get the spelling +/// location of. +static SourceLocation +getActualBoundsSafetyAttributeLocation(SourceManager &SrcMan, + SourceLocation AttrLoc) { + // Handles case of being passed as macro argument. + AttrLoc = SrcMan.getTopMacroCallerLoc(AttrLoc); + // Escapes ptrcheck.h macro. Loop needed for __null_terminated. + while (SrcMan.getFilename(SrcMan.getSpellingLoc(AttrLoc)) + .ends_with("ptrcheck.h")) { + AttrLoc = + SrcMan.getImmediateExpansionRange(AttrLoc).getAsRange().getBegin(); + } + // Handles case of being part of a macro body, making sure we get a location + // inside the macro. + AttrLoc = SrcMan.getSpellingLoc(AttrLoc); + return AttrLoc; +} + +/// @brief Checks whether there is a previous redundant -fbounds-safety attribute +/// that is part of the same declaration. We don't want to warn about redundant +/// attributes when the first occurrence is part of a typedef or macro body, +/// while the other is not. +static bool +shouldWarnRedundantBoundsSafetyAttribute(const TypeProcessingState &state, + const ParsedAttr &CurrAttr) { + auto &SrcMan = state.getSema().getSourceManager(); + + // If the declaration is part of a macro body, use the macro call site + // as the location of the declaration. + auto DeclLoc = SrcMan.getExpansionLoc(state.getDeclarator().getBeginLoc()); + auto CurrLoc = getActualBoundsSafetyAttributeLocation(SrcMan, CurrAttr.getLoc()); + + // Check whether the attribute is spelled out inside the declarator range. + // The attribute being spelled out outside the declarator range implies that + // it's added by a macro or typedef, and vice versa. + // We cannot use the declarator range and fullyContains() here because the + // declarator end location may not be set yet. Just comparing against the + // begin loc still works for this purpose, since macros and typedefs cannot be + // forward referenced. If the attribute location is after the start of the + // declaration it is inside the declarator range. + if (SrcMan.isBeforeInTranslationUnit(DeclLoc, CurrLoc)) { + for (auto &OtherAttr : state.getCurrentAttributes()) { + if (OtherAttr.getKind() != CurrAttr.getKind()) + continue; + SourceLocation OtherLoc = + getActualBoundsSafetyAttributeLocation(SrcMan, OtherAttr.getLoc()); + if (SrcMan.isBeforeInTranslationUnit(DeclLoc, OtherLoc) && + SrcMan.isBeforeInTranslationUnit(OtherLoc, CurrLoc)) { + return true; + } + } + } + + return false; +} + +/// Unpeel types until a PointerType is encountered and apply a bounds attribute +/// to it. Then rewrap it with the previous outer types. Uses TypeLoc when +/// possible to enable fetching the Attr* of AttributedTypes created in a +/// different DeclSpec than the one currently being processed. In theory +/// AttributedType can be created with raw attr::Kind instead Attr*, but while +/// processing decls we need to save the (AttributedType,Attr*) pair for later +/// TypeSourceInfo processing. When the TypeLoc isn't accessible a dummy +/// TypeLoc is created from the Type. +class ConstructBoundsSafetyPointerType + : public TypeLocVisitor { + using BaseClass = TypeLocVisitor; + +protected: + enum DiagBoundKind { + Bidi = 0, + Indexable = 1, + Single = 2, + Unsafe = 3, + }; + + TypeProcessingState &state; + Sema &S; + ASTContext &Ctx; + ParsedAttr &PAttr; + // Save type that was involved in removing context for rebuilding + // AttributedType + QualType ProblematicTy; + DiagBoundKind DiagIndex; + // Whether the current TypeLoc being processed is created by this class, or + // fetched from TypeSourceInfo. If true, the TypeLoc doesn't contain + // ExtraLocalData (i.e. Attr* for AttributedTypeLoc). + bool TypeLocIsForged = false; + // Errors related to the PointerType already having a bounds attribute should + // only be emitted if that attribute was explicitly added. + bool AutoBounded = false; + // Prevent the same atomics type error from being emitted twice if nested + // inside AttributedType + bool EmittedAtomicError = false; + BoundsSafetyPointerAttributes FAttrFromAttributedType; + + QualType copyQualifiers(QualType OldTy, QualType NewTy) const { + Qualifiers Qs = OldTy.getLocalQualifiers(); + if (const auto *AT = OldTy->getAs()) { + Qualifiers ModifiedTyQs = AT->getModifiedType().getQualifiers(); + Qualifiers::removeCommonQualifiers(Qs, ModifiedTyQs); + } + QualifierCollector QC(Qs); + return QC.apply(Ctx, NewTy); + } + +public: + explicit ConstructBoundsSafetyPointerType(TypeProcessingState &state, + ParsedAttr &PAttr) + : state(state), S(state.getSema()), Ctx(state.getSema().getASTContext()), + PAttr(PAttr) { + switch (PAttr.getKind()) { + case ParsedAttr::AT_PtrBidiIndexable: + DiagIndex = Bidi; + break; + case ParsedAttr::AT_PtrIndexable: + DiagIndex = Indexable; + break; + case ParsedAttr::AT_PtrSingle: + DiagIndex = Single; + break; + default: + assert(PAttr.getKind() == ParsedAttr::AT_PtrUnsafeIndexable); + DiagIndex = Unsafe; + } + } + + QualType Visit(const TypeLoc TL) { + QualType NewTy = BaseClass::Visit(TL); + return copyQualifiers(TL.getType(), NewTy); + } + + QualType VisitType(QualType T) { + assert(!isa(T)); + const DeclSpec &DS = state.getDeclarator().getDeclSpec(); + if (DS.isTypeRep()) + if (auto LIT = DS.getRepAsType().get()->getAs()) + if (LIT->getType().getAsOpaquePtr() == T.getAsOpaquePtr()) { + // This case is relevant for typeof(T) and _Atomic(type). When these + // declspecs have been parsed any Attrs inside the parentheses are + // cleared from the TypeProcessingState, but the inner TSI is saved in + // the TypeRep allowing us to fetch it again here. + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = false; + return Visit(LIT->getTypeSourceInfo()->getTypeLoc()); + } + // Create dummy TypeLoc when none is available to avoid duplicating visitors + TypeLoc TL(T, nullptr); + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = true; + return Visit(TL); + } + + QualType VisitTypeLoc(const TypeLoc TL) { + TypeLoc Next = TL.getNextTypeLoc(); + if (Next.isNull()) { + return {}; + } + return Visit(Next); + } + +// Given both TypeLoc and QualType, pick best option to visit. +// This is a macro so that expressions that would result in segfaults if +// evaluated can be passed. We detect cases where that could happen with +// TypeLocIsForged, preventing the evaluation. +#define VisitEither(TL, T) \ + (TypeLocIsForged ? VisitType(T) : VisitEitherImpl(TL, T)) + + // Keeping the rest of the implementation in this function prevents the + // parameters from being evaluated multiple times. + QualType VisitEitherImpl(TypeLoc TL, QualType T) { + assert(!TypeLocIsForged); + // The TypeLoc for an __auto_type can still contain the undeduced type even + // after the type has been deduced -> visit the correct type instead. + // Otherwise visit the TypeLoc if it is the real TypeLoc for the type. + if (TL.getTypePtr()->isUndeducedType() && !T->isUndeducedType()) + return VisitType(T); + return Visit(TL); + } + + QualType VisitAutoTypeLoc(const AutoTypeLoc TL) { + const AutoType *ATy = TL.getTypePtr(); + QualType DTy = ATy->getDeducedType(); + if (DTy.isNull()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_undeduced) << DiagIndex; + return {}; + } + + QualType Res = VisitEither(TL.getNextTypeLoc(), DTy); + if (Res.isNull()) + return {}; + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == TST_auto || TST == TST_decltype_auto || TST == TST_auto_type || + TST == TST_unspecified) + Res = Ctx.getAutoType(Res, TL.getAutoKeyword(), false); + return Res; + } + + QualType VisitTypeOfTypeLoc(const TypeOfTypeLoc TL) { + QualType Res = VisitEither(TL.getUnmodifiedTInfo()->getTypeLoc(), + TL.getUnmodifiedType()); + if (Res.isNull()) + return {}; + + // We don't want to actually wrap the modified type in typeof() since + // it's misleading, but if this is the current declspec we need the type + // to match the TST. + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == DeclSpec::TST_typeofType || + TST == DeclSpec::TST_typeof_unqualType) + Res = Ctx.getTypeOfType(Res, TL.getTypePtr()->getKind()); + return Res; + } + + QualType VisitTypeOfExprTypeLoc(const TypeOfExprTypeLoc TL) { + const TypeOfExprType *T = TL.getTypePtr(); + const Expr *E = T->getUnderlyingExpr()->IgnoreParens(); + + if (auto AddrOfE = dyn_cast(E); + AddrOfE && AddrOfE->getOpcode() == UO_AddrOf) { + if (auto DerefE = dyn_cast(AddrOfE->getSubExpr()); + DerefE && DerefE->getOpcode() == UO_Deref) { + E = DerefE->getSubExpr(); + } + } + + E = E->IgnoreParenLValueCasts(); + + // Finding an appropriate decl so we can get the TypeSourceInfo is not + // always possible, but this should cover the basic cases. The TSI is + // only needed for reconstructing AttributedType. Unlike some other types + // TypeOfExprType doesn't crash when attributes are missing, luckily. + const DeclaratorDecl *DDecl = nullptr; + if (auto DRE = dyn_cast(E)) + DDecl = dyn_cast(DRE->getDecl()); + else if (auto CallE = dyn_cast(E)) + DDecl = dyn_cast(CallE->getCalleeDecl()); + else if (auto ME = dyn_cast(E)) + DDecl = dyn_cast(ME->getMemberDecl()); + + TypeSourceInfo *TSI = nullptr; + if (DDecl) + TSI = DDecl->getTypeSourceInfo(); + else if (auto CE = dyn_cast(E)) + TSI = CE->getTypeInfoAsWritten(); + + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = TSI == nullptr; + + QualType Res = VisitEither(TSI->getTypeLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + if (Res.isNull()) + return {}; + // We don't want to actually wrap the modified type in typeof() since + // it's misleading, but if this is the current declspec we need the type + // to match the TST. + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == DeclSpec::TST_typeofExpr || + TST == DeclSpec::TST_typeof_unqualExpr) { + Expr *DummyExpr = ImplicitCastExpr::Create( + Ctx, Res, CK_BoundsSafetyPointerCast, T->getUnderlyingExpr(), + /*BasePath=*/nullptr, Expr::getValueKindForType(Res), + FPOptionsOverride()); + Res = Ctx.getTypeOfExprType(DummyExpr, TL.getTypePtr()->getKind()); + } + return Res; + } + + // If we reach a function type we arrived via typeof(call_expr()) and want the + // return type + QualType VisitFunctionProtoTypeLoc(const FunctionProtoTypeLoc FPTL) { + assert(!TypeLocIsForged); + return Visit(FPTL.getReturnLoc()); + } + + QualType VisitFunctionNoProtoTypeLoc(const FunctionNoProtoTypeLoc FPTL) { + assert(!TypeLocIsForged); + return Visit(FPTL.getReturnLoc()); + } + + QualType VisitParenTypeLoc(const ParenTypeLoc TL) { + QualType InnerTy = VisitEither( + TL.getInnerLoc(), TL.getType().getSingleStepDesugaredType(Ctx)); + return S.Context.getParenType(InnerTy); + } + + QualType VisitPointerTypeLoc(const PointerTypeLoc TL) { + QualType PTy = TL.getType(); + + BoundsSafetyPointerAttributes FAttr = TL.getPointerAttributes(); + auto BoundsAttrPrev = FAttr.getBoundsAttr(); + + const auto Kind = PAttr.getKind(); + + switch (Kind) { + case ParsedAttr::AT_PtrBidiIndexable: + FAttr.setBidiIndexable(); + break; + case ParsedAttr::AT_PtrSingle: + FAttr.setSingle(); + break; + case ParsedAttr::AT_PtrIndexable: + FAttr.setIndexable(); + break; + case ParsedAttr::AT_PtrUnsafeIndexable: + FAttr.setUnsafeIndexable(); + break; + default: + assert(0 && "Expected BoundsSafety Pointer Type Attr"); + return {}; + } + + if (!AutoBounded && (BoundsAttrPrev || FAttrFromAttributedType.getBoundsAttr())) { + if ((BoundsAttrPrev && BoundsAttrPrev != FAttr.getBoundsAttr()) || + (FAttrFromAttributedType.getBoundsAttr() && + FAttrFromAttributedType.getBoundsAttr() != FAttr.getBoundsAttr())) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* bound */ 0; + } else if (shouldWarnRedundantBoundsSafetyAttribute(state, PAttr)) { + S.Diag(PAttr.getLoc(), + diag::warn_bounds_safety_duplicate_pointer_attributes) + << 1 << BoundsAttrPrev - 1; + } + return {}; + } + + if (FAttr.hasUpperBound()) { + if (PTy->isFunctionPointerType()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_function_pointers_cannot_be_indexable); + return {}; + } + } + + // In attribute-only mode, represent __single and __unsafe_indexable as + // sugars. + if (S.getLangOpts().BoundsSafetyAttributes && + !S.getLangOpts().BoundsSafety && + (Kind == ParsedAttr::AT_PtrSingle || + Kind == ParsedAttr::AT_PtrUnsafeIndexable)) { + const Attr *A; + if (Kind == ParsedAttr::AT_PtrSingle) + A = createSimpleAttr(Ctx, PAttr); + else + A = createSimpleAttr(Ctx, PAttr); + return state.getAttributedType(A, PTy, PTy); + } + + return Ctx.getPointerType(PTy->getPointeeType(), FAttr); + } + + QualType + VisitCountAttributedTypeLoc(const CountAttributedTypeLoc TL) { + if (DiagIndex < Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_count_bound_attributes) + << PAttr << DiagIndex; + return TL.getType(); + } + + auto InnerTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + const CountAttributedType *DCPT = TL.getTypePtr(); + + return Ctx.getCountAttributedType( + InnerTy, DCPT->getCountExpr(), DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } + + QualType + VisitDynamicRangePointerTypeLoc(const DynamicRangePointerTypeLoc TL) { + if (DiagIndex < Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_count_bound_attributes) + << PAttr << DiagIndex; + return TL.getType(); + } + + auto InnerTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + + const DynamicRangePointerType *DRPT = TL.getTypePtr(); + return Ctx.getDynamicRangePointerType( + InnerTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } + + QualType VisitMacroQualifiedTypeLoc(const MacroQualifiedTypeLoc TL) { + QualType NewTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + return S.Context.getMacroQualifiedType( + NewTy, TL.getTypePtr()->getMacroIdentifier()); + } + + /// Typedef is a different declarator, so we won't have Attrs for their + /// AttributedType. Fetch the TypeLoc from the Decl in case we need to fetch + /// the Attr from the AttributedTypeLoc. + QualType VisitTypedefTypeLoc(const TypedefTypeLoc TL) { + TypeLoc ActualTypeLoc = + TL.getTypedefNameDecl()->getTypeSourceInfo()->getTypeLoc(); + + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = false; + QualType Res = VisitEither(ActualTypeLoc, + TL.getType().getSingleStepDesugaredType(Ctx)); + + if (Res.isNull()) + return {}; + + // We don't want to actually wrap the modified type in typedef() since it's + // misleading, but if this is the current declspec we need the type to match + // the TST. + if (state.getDeclarator().getDeclSpec().getTypeSpecType() == + DeclSpec::TST_typename) { + const char *Suffix; + switch (DiagIndex) { + case Bidi: + Suffix = " __bidi_indexable"; + break; + case Indexable: + Suffix = " __indexable"; + break; + case Single: + Suffix = " __single"; + break; + case Unsafe: + Suffix = " __unsafe_indexable"; + break; + } + // Create a dummy typedef with the new bounds. The bounds suffix is used + // to avoid clashing in case the same typedef is used multiple times with + // different bounds. The " " in the name makes it print as if the bounds + // were outside the typedef. This also has the nice side-effect that it + // cannot clash with typenames from source code. + DeclarationName DN(&Ctx.Idents.get( + (TL.getTypePtr()->getDecl()->getName() + Suffix).str())); + LookupResult Result(S, DN, PAttr.getLoc(), Sema::LookupOrdinaryName); + ; + if (S.LookupName(Result, S.TUScope, /*AllowBuiltinCreation=*/true)) { + NamedDecl *ND = Result.getFoundDecl(); + if (TypedefNameDecl *TD = dyn_cast(ND)) + if (TD->getUnderlyingType() == Res) + return S.Context.getTypedefType(TD); + } + TypedefDecl *NewTypedef = Ctx.buildImplicitTypedef(Res, DN.getAsString()); + S.IdResolver.AddDecl(NewTypedef); + Res = Ctx.getTypedefType(NewTypedef, Res); + } + return Res; + } + + QualType VisitAttributedTypeLoc(const AttributedTypeLoc TL) { + const AttributedType *T = TL.getTypePtr(); + + SaveAndRestore SARAutoBounded(AutoBounded); + AutoBounded |= T->getAttrKind() == attr::PtrAutoAttr; + + SaveAndRestore SARFAttrFromAttributedtype( + FAttrFromAttributedType); + if (T->getAttrKind() == attr::PtrSingle) + FAttrFromAttributedType = BoundsSafetyPointerAttributes::single(); + else if (T->getAttrKind() == attr::PtrUnsafeIndexable) + FAttrFromAttributedType = BoundsSafetyPointerAttributes::unsafeIndexable(); + + QualType NewEqTy = VisitType(T->getEquivalentType()); + + // Early exit so that Visit(ModifiedLoc) doesn't emit the same error + if (NewEqTy.isNull()) + return {}; + + if (T->getAttrKind() == attr::PtrAutoAttr) + return NewEqTy; + + const Attr *attr = TypeLocIsForged + ? state.getAttrForAttributedType(TL.getTypePtr()) + : TL.getAttr(); + // If we create an AttributedType without the corresponding Attr we'll run + // into problems when TypLocs are created, however there are cases when + // dropping the AttributedType would also cause a crash during later TypeLoc + // processing. Emit an error to prevent the crash in the rare case that we + // cannot recover the Attr. + if (!attr) { + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_irrecoverable_attr) + << DiagIndex << TL.getType() + << (TST == TST_typeofExpr || TST == TST_typeof_unqualExpr); + return {}; + } + + if (T->getModifiedType().getAsOpaquePtr() == + T->getEquivalentType().getAsOpaquePtr()) + return state.getAttributedType(attr, NewEqTy, NewEqTy); + + QualType NewModTy = + VisitEither(TL.getModifiedLoc(), TL.getTypePtr()->getModifiedType()); + if (NewModTy.isNull()) + return {}; + return state.getAttributedType(attr, NewModTy, NewEqTy); + } + + QualType VisitAtomicTypeLoc(const AtomicTypeLoc TL) { + if (DiagIndex < Single && !EmittedAtomicError) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << (DiagIndex == Bidi ? 1 : 0); + // Not marking the bounds as explicit (preventing + // PtrAutoAttr) will cause the error to be emitted a second time for local + // variables when the default __bidi_indexable bounds are applied, so go + // on and apply the bounds despite the error. + EmittedAtomicError = true; + } + + QualType VT = + VisitEither(TL.getValueLoc(), TL.getTypePtr()->getValueType()); + + if (VT.isNull()) + return {}; + + return Ctx.getAtomicType(VT); + } + + QualType VisitValueTerminatedTypeLoc(const ValueTerminatedTypeLoc TL) { + if (DiagIndex != Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + return TL.getType(); + } + + QualType T = VisitEither(TL.getNextTypeLoc(), TL.getTypePtr()->desugar()); + + if (T.isNull()) + return {}; + + return Ctx.getValueTerminatedType(T, TL.getTypePtr()->getTerminatorExpr()); + } +#undef VisitEither +}; + +/// Handle BoundsSafety pointer type attributes - +/// bidi_indexable, single, indexable, unsafe_indexable. +static void HandleBoundsSafetyPointerTypeAttr(TypeProcessingState &state, + ParsedAttr &PAttr, QualType &type) { + Sema &S = state.getSema(); + + bool AttrSupported = false; + if (S.getLangOpts().BoundsSafety) { + AttrSupported = true; + } else if (S.getLangOpts().BoundsSafetyAttributes) { + auto K = PAttr.getKind(); + AttrSupported = + K == ParsedAttr::AT_PtrSingle || K == ParsedAttr::AT_PtrUnsafeIndexable; + } + + /// Unlike DeclAttrs which are by default skipped with unsupported LangOpts, + /// TypeAttrs require manual code to ignore unsupported attribute. + if (!AttrSupported) { + S.Diag(PAttr.getLoc(), diag::warn_attribute_ignored) << PAttr; + PAttr.setInvalid(); + return; + } + + QualType T = type; + if (auto AT = T->getAs()) + T = AT->getValueType(); + + if (!T->isPointerType() && !T->getAs()) { + S.Diag(PAttr.getLoc(), diag::err_attribute_pointers_only) << PAttr << 0; + PAttr.setInvalid(); + return; + } + + switch (PAttr.getKind()) { + case ParsedAttr::AT_PtrBidiIndexable: + case ParsedAttr::AT_PtrIndexable: + if (type.getPointerAuth()) { + PAttr.setInvalid(); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_ptrauth_on_indexable_pointer); + } + break; + default: + break; + } + + QualType NewTy = ConstructBoundsSafetyPointerType(state, PAttr).VisitType(type); + if (!NewTy.isNull()) + type = NewTy; + else + PAttr.setInvalid(); +} + +static bool HandlePtrTerminatedByTypeAttr(TypeProcessingState &state, + const ParsedAttr &PAttr, + QualType &type) { + Sema &S = state.getSema(); + QualType T = type; + + if (auto AT = type->getAs()) { + auto ModifiedPlusVT = AT->getModifiedType(); + if (!HandlePtrTerminatedByTypeAttr(state, PAttr, ModifiedPlusVT)) + return false; // Avoid emitting errors twice + + auto EquivalentPlusVT = AT->getEquivalentType(); + if (!HandlePtrTerminatedByTypeAttr(state, PAttr, EquivalentPlusVT)) + return false; + + auto QualsOnT = type.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + + const Attr *attr = nullptr; + if (auto TDT = type->getAs()) { + // Make sure the AttributedType is inside the TypedefType and not vice + // versa. + if (TDT->getAs() == AT) { + auto ATLoc = TDT->getDecl() + ->getTypeSourceInfo() + ->getTypeLoc() + .getAsAdjusted(); + attr = ATLoc.getAttr(); + } + } + if (!attr) + attr = state.getAttrForAttributedType(AT); + type = state.getAttributedType(attr, ModifiedPlusVT, EquivalentPlusVT); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + type = QC.apply(state.getSema().Context, type); + } + return true; + } + + if (!S.getLangOpts().BoundsSafetyAttributes) { + S.Diag(PAttr.getLoc(), diag::warn_attribute_ignored) << PAttr; + PAttr.setInvalid(); + return false; + } + + if (PAttr.getNumArgs() != 1) { + S.Diag(PAttr.getLoc(), diag::err_attribute_wrong_number_arguments) + << PAttr << 1; + PAttr.setInvalid(); + return false; + } + + auto *TerminatorExpr = PAttr.getArgAsExpr(0); + + // The terminator must be an ICE. + llvm::APSInt Terminator; + if (!verifyValidIntegerConstantExpr(S, PAttr, Terminator)) { + PAttr.setInvalid(); + return false; + } + + const auto *VT = T->getAs(); + if (VT) { + llvm::APSInt TermVal = VT->getTerminatorValue(S.Context); + if (!llvm::APSInt::isSameValue(TermVal, Terminator)) { + assert(T->isPointerType() || T->isArrayType()); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_conflicting_pointer_attributes) + << T->isPointerType() << /* terminator */ 4; + S.Diag(PAttr.getLoc(), + diag::note_bounds_safety_conflicting_pointer_attribute_args) + << /* terminator */ 2 << VT->getTerminatorExpr() << TerminatorExpr; + PAttr.setInvalid(); + return false; + } + + if (shouldWarnRedundantBoundsSafetyAttribute(state, PAttr)) { + S.Diag(PAttr.getLoc(), diag::warn_bounds_safety_duplicate_pointer_attributes) + << T->isPointerType() << 4; + PAttr.setInvalid(); + return false; + } + + SplitQualType Split = T.getSplitUnqualifiedType(); + T = S.Context.getQualifiedType(VT->desugar(), Split.Quals); + } + + if (!T->isConstantArrayType() && !T->isIncompleteArrayType() && + !T->isPointerType()) { + if (T->isAtomicType()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << /*terminated_by*/ 7; + } else { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_terminated_by_wrong_type); + } + PAttr.setInvalid(); + return false; + } + + QualType PET; + + if (const auto *AT = S.Context.getAsArrayType(T)) { + // Constant arrays cannot be empty. We will check incomplete arrays during + // initialization. + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + if (CAT->getSize().isZero()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_terminated_by_empty_array); + PAttr.setInvalid(); + return false; + } + } + PET = AT->getElementType(); + } + + if (const auto *PT = T->getAs()) { + // Pointers with dynamic bounds are normally handled later, but they + // can surface when using __typeof__, or probably typedefs eventually. + if (T->isBoundsAttributedType()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + PAttr.setInvalid(); + return false; + } + + // If the pointer is unspecified, we will add __single attribute later in + // MakeAutoPointer. + if (!PT->isUnspecified() && !PT->isSingle()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + PAttr.setInvalid(); + return false; + } + PET = PT->getPointeeType(); + } + + // The pointee must be an integer or a thin pointer. + if (!(PET->isIntegralOrEnumerationType() || + (PET->isPointerType() && !PET->isPointerTypeWithBounds()))) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_elem_or_pointee_type) + << T->isPointerType(); + PAttr.setInvalid(); + return false; + } + + ExprResult Res(TerminatorExpr); + CastKind Kind = S.PrepareScalarCast(Res, PET); + Res = S.ImpCastExprToType(TerminatorExpr, PET, Kind); + if (!Res.get()) { + PAttr.setInvalid(); + return false; + } + TerminatorExpr = Res.get(); + + type = S.Context.getValueTerminatedType(T, TerminatorExpr); + +#ifndef NDEBUG + if (VT) { + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + VT->Profile(OldID, S.Context); + type->getAs()->Profile(NewID, S.Context); + if (OldID != NewID) { + VT->dump(); + type->dump(); + } + assert(OldID == NewID); + } +#endif + + return true; +} + +static void HandleArrayDecayDiscardsCountInParametersAttr( + TypeProcessingState &state, ParsedAttr &Attr, QualType &type) { + // only valid with -fbounds-safety enabled + if (!state.getSema().getLangOpts().BoundsSafetyAttributes) { + state.getSema().Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr; + Attr.setInvalid(); + return; + } + + ASTContext &Context = state.getSema().Context; + // only valid on array types + if (!Context.getAsArrayType(type)) { + state.getSema().Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr; + Attr.setInvalid(); + return; + } + + type = state.getAttributedType( + createSimpleAttr(Context, Attr), + type, type); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Handle OpenCL Access Qualifier Attribute. static void HandleOpenCLAccessAttr(QualType &CurType, const ParsedAttr &Attr, Sema &S) { @@ -8857,6 +9754,22 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, attr.setUsedAsTypeAttr(); break; + /*TO_UPSTREAM(BoundsSafet) ON*/ + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + HandleBoundsSafetyPointerTypeAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + + case ParsedAttr::AT_PtrTerminatedBy: + HandlePtrTerminatedByTypeAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + + case ParsedAttr::AT_ArrayDecayDiscardsCountInParameters: + HandleArrayDecayDiscardsCountInParametersAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + /*TO_UPSTREAM(BoundsSafet) OFF*/ NULLABILITY_TYPE_ATTRS_CASELIST: // Either add nullability here or try to distribute it. We @@ -8965,14 +9878,28 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, // applied to ObjC builtin attributes. if (isa(type) && attr.hasMacroIdentifier() && !type.getQualifiers().hasObjCLifetime() && - !type.getQualifiers().hasObjCGCAttr() && - attr.getKind() != ParsedAttr::AT_ObjCGC && - attr.getKind() != ParsedAttr::AT_ObjCOwnership) { - const IdentifierInfo *MacroII = attr.getMacroIdentifier(); - type = state.getSema().Context.getMacroQualifiedType(type, MacroII); - state.setExpansionLocForMacroQualifiedType( - cast(type.getTypePtr()), - attr.getMacroExpansionLoc()); + !type.getQualifiers().hasObjCGCAttr()) { + + switch(attr.getKind()) { + case ParsedAttr::AT_ObjCGC: + case ParsedAttr::AT_ObjCOwnership: + /* TO_UPSTREAM(BoundsSafety) ON*/ + case ParsedAttr::AT_CountedBy: + case ParsedAttr::AT_SizedBy: + case ParsedAttr::AT_CountedByOrNull: + case ParsedAttr::AT_SizedByOrNull: + case ParsedAttr::AT_PtrEndedBy: + case ParsedAttr::AT_PtrTerminatedBy: + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + /* TO_UPSTREAM(BoundsSafety) OFF*/ + break; + default: + const IdentifierInfo *MacroII = attr.getMacroIdentifier(); + type = state.getSema().Context.getMacroQualifiedType(type, MacroII); + state.setExpansionLocForMacroQualifiedType( + cast(type.getTypePtr()), + attr.getMacroExpansionLoc()); + }; } } } @@ -9544,9 +10471,518 @@ QualType Sema::BuildTypeofExprType(Expr *E, TypeOfKind Kind) { if (const TagType *TT = T->getAs()) DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc()); } + + /*TO_UPSTREAM(BoundsSafety) ON*/ + // We don't have enough context to create a DBPT with coupled declarations + // from here as we need to know the scope of the new declaration (if this is + // for a new declaration). Error out unless `getNumCoupledDecls()` is 0, + // as DBPTs without coupled decls are not scope-dependent. + if (auto DBPT = E->getType()->getAs()) { + if (DBPT->getNumCoupledDecls() != 0) { + Diag(E->getExprLoc(), diag::err_bounds_safety_typeof_dbpt) << E->getType(); + // return a bare pointer for recovery purposes + return QualType(E->getType()->getAs(), 0); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return Context.getTypeOfExprType(E, Kind); } +/* TO_UPSTREAM(BoundsSafety) ON */ +namespace { + +Expr *UnwrapDerefAddrOfPairs(Expr *E) { + UnaryOperator *UO = dyn_cast(E); + if (!UO) + return E; + switch (UO->getOpcode()) { + case UO_Deref: + UO = dyn_cast(UO->getSubExpr()->IgnoreParens()); + if (!UO || UO->getOpcode() != UO_AddrOf) + return E; + break; + case UO_AddrOf: + UO = dyn_cast(UO->getSubExpr()->IgnoreParens()); + if (!UO || UO->getOpcode() != UO_Deref) + return E; + break; + default: + return E; + } + /// XXX: This way we are leaving out '*((int *)&ch)'. + return UnwrapDerefAddrOfPairs(UO->getSubExpr()->IgnoreParens()); +} + +/// CountArgChecker - performs sanity checks for an argument in +/// '__counted_by()' or '__sized_by()', and returns a constant-folded +/// count expression if feasible. +class CountArgChecker : public TreeTransform { + using BaseTransform = TreeTransform; + using DeclList = llvm::SmallVector; + using DeclSet = llvm::SmallSet; + DeclList &Dependees; + bool CountInBytes : 1; + bool OrNull : 1; + bool ScopeCheck : 1; + bool IsArray : 1; + DeclSet Visited; + bool InDeref = false; + BinaryOperator *VisitedBinOp = nullptr; + +private: + ExprResult Fallback(Expr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + return ExprError(); + } + + IntegerLiteral *ConstantFoldOrNull(Expr *E) { + if (E->isValueDependent()) + return nullptr; + + Expr::EvalResult Eval; + /// EvaluateAsInt is enough because nodes are visited in-order and + /// we require the final results be integer. Hence, cases like + /// '(int)(float)f' should work. + if (E->EvaluateAsInt(Eval, SemaRef.Context)) + return IntegerLiteral::Create(SemaRef.Context, Eval.Val.getInt(), + E->getType(), E->getExprLoc()); + return nullptr; + } + + BoundsAttributedType::BoundsAttrKind getDynamicCountKind() { + return CountInBytes ? (OrNull ? CountAttributedType::SizedByOrNull + : CountAttributedType::SizedBy) + : (OrNull ? CountAttributedType::CountedByOrNull + : CountAttributedType::CountedBy); + } + +public: + explicit CountArgChecker(Sema &S, DeclList &DList, bool CountInBytes, + bool OrNull, bool ScopeCheck, bool IsArray) + : BaseTransform(S), Dependees(DList), CountInBytes(CountInBytes), + OrNull(OrNull), ScopeCheck(ScopeCheck), IsArray(IsArray) {} + + ExprResult TransformExpr(Expr *E) { + switch (E->getStmtClass()) { + +#define TRANSFORM_EXPR(Node) \ + case Stmt::Node##Class: \ + return Transform##Node(cast(E)); + +#define FORWARD_EXPR(Node) \ + case Stmt::Node##Class: \ + return E; + + TRANSFORM_EXPR(CallExpr) + TRANSFORM_EXPR(ImplicitCastExpr) + TRANSFORM_EXPR(CStyleCastExpr) + TRANSFORM_EXPR(ParenExpr) + TRANSFORM_EXPR(BinaryOperator) + TRANSFORM_EXPR(UnaryOperator) + TRANSFORM_EXPR(DeclRefExpr) + TRANSFORM_EXPR(MemberExpr) + FORWARD_EXPR(IntegerLiteral) + FORWARD_EXPR(FloatingLiteral) + FORWARD_EXPR(FixedPointLiteral) + +#undef TRANSFORM_EXPR +#undef FORWARD_EXPR + default: + break; + } + return Fallback(E); + } + + ExprResult TransformCallExpr(CallExpr *E) { + const auto *Callee = E->getDirectCallee(); + if (Callee && Callee->hasAttr()) { + for (auto *Arg : E->arguments()) { + if (!Arg->isEvaluatable(SemaRef.Context)) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_dynamic_count_function_call_argument) + << E << getDynamicCountKind(); + return ExprError(); + } + } + return E; + } + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_dynamic_count_function_call) + << getDynamicCountKind(); + if (Callee) { + SemaRef.Diag(Callee->getLocation(), diag::note_callee_decl) << Callee; + } + return ExprError(); + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *E) { + return BaseTransform::TransformImplicitCastExpr(E); + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *E) { + Expr::EvalResult Eval; + /// EvaluateAsInt is enough because nodes are visited in-order and + /// we require the final results be integer. Hence, cases like + /// '(int)(float)f' should work. + if (!E->EvaluateAsInt(Eval, SemaRef.Context)) + return BaseTransform::TransformCStyleCastExpr(E); + return IntegerLiteral::Create(SemaRef.Context, Eval.Val.getInt(), + E->getType(), E->getExprLoc()); + ; + } + + ExprResult TransformParenExpr(ParenExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + return BaseTransform::TransformParenExpr(E); + } + + ExprResult TransformBinaryOperator(BinaryOperator *E) { + VisitedBinOp = E; + if (auto CF = ConstantFoldOrNull(E)) + return CF; + return BaseTransform::TransformBinaryOperator(E); + } + + /// Add support for UO_Deref in particular to support out parameters. + /// e.g., 'void foo(int *__counted_by(len)* out_buf, int len)' + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + Expr *UnwrappedExpr = UnwrapDerefAddrOfPairs(E); + E = dyn_cast(UnwrappedExpr); + if (!E) + return TransformExpr(UnwrappedExpr); + + if (E->getOpcode() == UO_Deref && !InDeref) { + if (VisitedBinOp) + return Fallback(E); + SaveAndRestore InDerefLocal(InDeref, true); + Expr *SubExpr = E->getSubExpr()->IgnoreParenCasts(); + if (auto *DR = dyn_cast(SubExpr)) { + if (!isa(DR->getDecl()) || ScopeCheck) { + SemaRef.Diag(E->getExprLoc(), + diag::err_deref_in_bounds_safety_count_non_parm_decl) + << getDynamicCountKind(); + return E; + } + return BaseTransform::TransformUnaryOperator(E); + } + return Fallback(E); + } + if (E->isArithmeticOp()) + return BaseTransform::TransformUnaryOperator(E); + + return Fallback(E); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + ValueDecl *VD = E->getDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) { + Dependees.push_back(TypeCoupledDeclRefInfo(VD, InDeref)); + } + return E; + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + // For structs/classes in C++, referring to a field creates a MemberExpr + // instead of DeclRefExpr. To support other parts of bounds-safety logic, + // replace the MemberExpr by DeclRefExpr. + // TODO: Check if this works for FAMs. + // TODO: Add support for MemberExpr (rdar://134311605). + if (!IsArray && SemaRef.getLangOpts().CPlusPlus) { + ValueDecl *VD = E->getMemberDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) + Dependees.push_back(TypeCoupledDeclRefInfo(VD, InDeref)); + Expr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc{}, SourceLocation{}, VD, + false, DeclarationNameInfo{VD->getDeclName(), VD->getLocation()}, + VD->getType(), VK_LValue); + return DRE; + } + + if (!IsArray) { + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + SemaRef.Diag(E->getOperatorLoc(), + diag::note_bounds_safety_struct_fields_only_in_fam); + return ExprError(); + } + + if (E->isArrow()) { + SemaRef.Diag(E->getExprLoc(), diag::error_bounds_safety_no_arrow_members); + return ExprError(); + } + + // Recursive call adds base decls to Dependees, i.e. for `a.b.c` it adds `a` + // and `b`. `c` is added by the call to Dependees.push_back() further down. + ExprResult BaseRes = TransformExpr(E->getBase()); + if (BaseRes.isInvalid()) + return ExprError(); + + if (BaseRes.get()->getType()->isUnionType()) { + SemaRef.Diag(E->getExprLoc(), diag::error_bounds_safety_no_count_in_unions) + << BaseRes.get() << BaseRes.get()->getType(); + return ExprError(); + } + + ValueDecl *VD = E->getMemberDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) { + Dependees.push_back( + TypeCoupledDeclRefInfo(VD, InDeref, /* Member */ true)); + } + + return E; + } +}; + +/// RangeArgChecker - performs sanity checks for an argument in '__ended_by()'. +/// Moreover, this checker extracts the dependent decl in the 'end' expression +/// of '__ended_by(end)'. +class RangeArgChecker : public TreeTransform { + using BaseTransform = TreeTransform; + std::optional DependeeInfo; + bool ScopeCheck; + bool InDeref = false; + + void setDependeeInfo(ValueDecl *VD, bool Deref) { + assert(!DependeeInfo.has_value()); + DependeeInfo = TypeCoupledDeclRefInfo(VD, Deref); + } + +public: + explicit RangeArgChecker(Sema &SemaRef, bool ScopeCheck) + : BaseTransform(SemaRef), ScopeCheck(ScopeCheck) {} + + ExprResult TransformExpr(Expr *E) { + switch (E->getStmtClass()) { + +#define TRANSFORM_EXPR(Node) \ + case Stmt::Node##Class: \ + return Transform##Node(cast(E)); + + TRANSFORM_EXPR(ImplicitCastExpr) + TRANSFORM_EXPR(ParenExpr) + TRANSFORM_EXPR(UnaryOperator) + TRANSFORM_EXPR(DeclRefExpr) + TRANSFORM_EXPR(MemberExpr) + +#undef TRANSFORM_EXPR + default: + break; + } + + return HandleInvalidExpr(E); + } + + ExprResult HandleInvalidExpr(Expr *E) { + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + return ExprError(); + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *E) { + return BaseTransform::TransformImplicitCastExpr(E); + } + + ExprResult TransformParenExpr(ParenExpr *E) { + return BaseTransform::TransformParenExpr(E); + } + + ExprResult TransformUnaryOperator(UnaryOperator *E) { + Expr *UnwrappedExpr = UnwrapDerefAddrOfPairs(E); + E = dyn_cast(UnwrappedExpr); + if (!E) + return TransformExpr(UnwrappedExpr); + + // No support for unary operators other than UO_Deref nor multiple UO_Deref. + if (E->getOpcode() != UO_Deref || InDeref) + return HandleInvalidExpr(E); + + if (ScopeCheck) { + // We support deref operator for parameters, check if the `end` decl is + // also a parameter. + const auto *DRE = + dyn_cast(E->getSubExpr()->IgnoreParenCasts()); + if (!DRE || !isa(DRE->getDecl())) { + SemaRef.Diag(E->getExprLoc(), + diag::err_deref_in_bounds_safety_count_non_parm_decl) + << 4 /* __ended_by */; + // Return ExprError() instead of calling HandleInvalidExpr(E) to avoid + // emitting another error. + return ExprError(); + } + } + + SaveAndRestore InDerefLocal(InDeref, true); + return BaseTransform::TransformUnaryOperator(E); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + // If the end pointer is not __single because of our implicit pointer + // annotation, we fix that during rebuilding the type of the end pointer. + QualType Ty = E->getType(); + if (!Ty->isSinglePointerType() && !Ty->isUnspecifiedPointerType() && + !Ty->isDynamicRangePointerType() && !Ty->hasAttr(attr::PtrAutoAttr)) { + SemaRef.Diag(E->getExprLoc(), diag::err_bounds_safety_end_pointer_single); + return ExprError(); + } + + setDependeeInfo(E->getDecl(), InDeref); + return E; + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + // Using `__ended_by(end)` annotation on a struct/class member where `end` + // refers to another member creates a MemberExpr with an implicit this in + // C++. We don't support MemberExpr in general, so here we replace the + // MemberExpr with an implicit this by a DeclRefExpr to the field in order + // to have the same representation as in C. + + if (!E->isImplicitAccess()) + return HandleInvalidExpr(E); + + ValueDecl *VD = E->getMemberDecl(); + setDependeeInfo(VD, InDeref); + Expr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc{}, SourceLocation{}, VD, false, + DeclarationNameInfo{VD->getDeclName(), VD->getLocation()}, + VD->getType(), VK_LValue); + return DRE; + } + + TypeCoupledDeclRefInfo getDependeeInfo() const { + return DependeeInfo.value(); + } +}; + +} // end anonymous namespace + +QualType Sema::BuildCountAttributedType(QualType PointerTy, Expr *CountExpr, + bool CountInBytes, bool OrNull, + bool ScopeCheck) { + + llvm::SmallVector Decls; + ExprResult R = CountArgChecker(*this, Decls, CountInBytes, OrNull, ScopeCheck, + PointerTy->isArrayType()) + .TransformExpr(CountExpr); + /// When the resulting expression is invalid, we still create the AST using + /// the original count expression for the sake of AST dump. + if (!R.isInvalid()) + CountExpr = R.get(); + R = DefaultLvalueConversion(CountExpr); + if (!R.isInvalid()) + CountExpr = R.get(); + + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !PointerTy->isSinglePointerType()) { + PointerTy = Context.getBoundsSafetyPointerType( + PointerTy, BoundsSafetyPointerAttributes::single()); + } + + return Context.getCountAttributedType( + PointerTy, CountExpr, CountInBytes, OrNull, + llvm::ArrayRef(Decls.begin(), Decls.end())); +} + +namespace { + +QualType DropAutoNullTerminated(ASTContext &Ctx, QualType T) { + if (!T->isImplicitlyNullTerminatedType(Ctx)) + return T; + + std::function DropPtrAutoNullTerminatedAttr; + DropPtrAutoNullTerminatedAttr = [&](QualType T) -> QualType { + const auto *AT = T->getAs(); + if (!AT) { + auto *VTT = T->getAs(); + assert(VTT); + return VTT->desugar(); + } + + QualType ModifiedTy = DropPtrAutoNullTerminatedAttr(AT->getModifiedType()); + if (AT->getAttrKind() == attr::PtrAutoNullTerminatedAttr) { + return ModifiedTy; + } + QualType EquivalentTy = + DropPtrAutoNullTerminatedAttr(AT->getEquivalentType()); + return Ctx.getAttributedType(AT->getAttrKind(), ModifiedTy, EquivalentTy); + }; + + return DropPtrAutoNullTerminatedAttr(T); +} + +} // namespace + +QualType Sema::BuildDynamicRangePointerType(QualType PointerTy, Expr *StartPtr, + Expr *EndPtr, bool ScopeCheck) { + std::optional StartDecl, EndDecl; + + if (StartPtr) { + RangeArgChecker RAC(*this, ScopeCheck); + ExprResult Res = RAC.TransformExpr(StartPtr); + if (Res.isInvalid()) + return QualType(); + StartPtr = Res.get(); + StartDecl = RAC.getDependeeInfo(); + } + + if (EndPtr) { + RangeArgChecker RAC(*this, ScopeCheck); + ExprResult Res = RAC.TransformExpr(EndPtr); + if (Res.isInvalid()) + return QualType(); + EndPtr = Res.get(); + EndDecl = RAC.getDependeeInfo(); + } + + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !PointerTy->isSinglePointerType()) { + PointerTy = Context.getBoundsSafetyPointerType( + PointerTy, BoundsSafetyPointerAttributes::single()); + } + + if (EndDecl.has_value()) { + ValueDecl *VD = EndDecl->getDecl(); + QualType Ty = VD->getType(); + if (Ty->hasAttr(attr::PtrAutoNullTerminatedAttr) && + Ty->isValueTerminatedType()) { + assert(Ty->getAs() + ->getTerminatorValue(Context) + .isZero()); + QualType NewTy = DropAutoNullTerminated(Context, Ty); + VD->setType(NewTy); + } + } + + return Context.getDynamicRangePointerType( + PointerTy, StartPtr, EndPtr, + StartDecl.has_value() ? ArrayRef(*StartDecl) + : ArrayRef(), + EndDecl.has_value() ? ArrayRef(*EndDecl) + : ArrayRef()); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + static void BuildTypeCoupledDecls(Expr *E, llvm::SmallVectorImpl &Decls) { @@ -9741,10 +11177,14 @@ QualType Sema::BuiltinEnumUnderlyingType(QualType BaseType, } QualType Sema::BuiltinAddPointer(QualType BaseType, SourceLocation Loc) { + /* TO_UPSTREAM(BoundsSafety) ON */ + // Declare and pass `BoundsSafetyPointerAttributes A` + BoundsSafetyPointerAttributes A; QualType Pointer = BaseType.isReferenceable() || BaseType->isVoidType() - ? BuildPointerType(BaseType.getNonReferenceType(), Loc, - DeclarationName()) + ? BuildPointerType(BaseType.getNonReferenceType(), A, + Loc, DeclarationName()) : BaseType; + /* TO_UPSTREAM(BoundsSafety) OFF */ return Pointer.isNull() ? QualType() : Pointer; } @@ -9994,6 +11434,28 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) { return QualType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) { + assert(!T->isBoundsAttributedType()); + int DiagIndex = -1; + if (T->isValueTerminatedType()) { + DiagIndex = 7; + } else if (const auto *PT = T->getAs()) { + BoundsSafetyPointerAttributes FAttr = PT->getPointerAttributes(); + if (!FAttr.isUnspecified() && !FAttr.isSingle() && + !FAttr.isUnsafeIndexable()) { + assert(FAttr.isIndexable() || FAttr.isBidiIndexable()); + DiagIndex = FAttr.isIndexable() ? 0 : 1; + } + } + if (DiagIndex != -1) { + Diag(Loc, diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + return QualType(); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // FIXME: Do we need any handling for ARC here? } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 14eb342f69b85..34ef2e59d54be 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -132,6 +132,12 @@ class TreeTransform { /// rather than in the subclass (e.g., lambda closure types). llvm::DenseMap TransformedLocalDecls; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// When MaterializeSequenceExpr is stripped, its children OpaqueValueExprs + /// should also be removed and all the subexpressions of the OVEs should + /// also be unwrapped. + llvm::SmallPtrSet RemovedOVEs; + /* TO_UPSTREAM(BoundsSafety) OFF*/ public: /// Initializes a new tree transformer. TreeTransform(Sema &SemaRef) : SemaRef(SemaRef) { } @@ -835,7 +841,9 @@ class TreeTransform { /// /// By default, performs semantic analysis when building the pointer type. /// Subclasses may override this routine to provide different behavior. - QualType RebuildPointerType(QualType PointeeType, SourceLocation Sigil); + QualType RebuildPointerType(QualType PointeeType, + BoundsSafetyPointerAttributes Attr, + SourceLocation Sigil); /// Build a new block pointer type given its pointee type. /// @@ -2983,9 +2991,13 @@ class TreeTransform { /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildCompoundLiteralExpr(SourceLocation LParenLoc, - TypeSourceInfo *TInfo, - SourceLocation RParenLoc, - Expr *Init) { + TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *Init, + /*TO_UPSTREAM(BoundsSafety) ON */ + bool IsFileScope + /*TO_UPSTREAM(BoundsSafety) OFF */ + ) { + return getSema().BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, Init); } @@ -4034,6 +4046,59 @@ class TreeTransform { return getSema().CreateRecoveryExpr(BeginLoc, EndLoc, SubExprs, Type); } + /// Build a BoundsSafety '__builtin_unsafe_forge_bidi_indexable' or + /// '__builtin_forge_single' expression. + ExprResult RebuildForgePtrExpr(SourceLocation KWLoc, QualType ResultType, + Expr *Addr, Expr *Size, + SourceLocation RParenLoc) { + return getSema().BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, + Size); + } + + /// Build a BoundsSafety '__builtin_get_pointer_{lower,upper}_bound' expression. + ExprResult RebuildGetBoundExpr(SourceLocation BeginLoc, SourceLocation EndLoc, + Expr *SubExpr, GetBoundExpr::BoundKind Kind, + bool RawPointer) { + return Kind == GetBoundExpr::BK_Lower + ? getSema().BuildLowerBoundExpr(SubExpr, BeginLoc, EndLoc, RawPointer) + : getSema().BuildUpperBoundExpr(SubExpr, BeginLoc, EndLoc, RawPointer); + } + + ExprResult + RebuildPredefinedBoundsCheckExpr(Expr *GuardedExpr, BoundsCheckKind Kind, + llvm::ArrayRef CheckArgs) { + return getSema().BuildPredefinedBoundsCheckExpr(GuardedExpr, Kind, + CheckArgs); + } + + /// Build a BoundsSafety bounds check expression. + ExprResult + RebuildBoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + llvm::ArrayRef CommonExprs) { + return getSema().BuildBoundsCheckExpr(GuardedExpr, Cond, CommonExprs); + } + + ExprResult RebuildMaterializeSequenceExpr(Expr *Wrapped, + ArrayRef Values) { + return getSema().BuildMaterializeSequenceExpr(Wrapped, Values); + } + + ExprResult RebuildTerminatedByToIndexableExpr(SourceLocation BeginLoc, + SourceLocation EndLoc, + Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator) { + return getSema().BuildTerminatedByToIndexableExpr( + PointerExpr, TerminatorExpr, IncludeTerminator, BeginLoc, EndLoc); + } + + ExprResult RebuildTerminatedByFromIndexableExpr( + SourceLocation BeginLoc, SourceLocation EndLoc, Expr *TerminatorExpr, + Expr *PointerExpr, Expr *PointerToTerminatorExpr) { + return getSema().BuildTerminatedByFromIndexableExpr( + TerminatorExpr, PointerExpr, PointerToTerminatorExpr, BeginLoc, EndLoc); + } + StmtResult RebuildOpenACCComputeConstruct(OpenACCDirectiveKind K, SourceLocation BeginLoc, SourceLocation DirLoc, @@ -5320,7 +5385,8 @@ QualType TreeTransform::TransformPointerType(TypeLocBuilder &TLB, if (getDerived().AlwaysRebuild() || PointeeType != TL.getPointeeLoc().getType()) { - Result = getDerived().RebuildPointerType(PointeeType, TL.getSigilLoc()); + Result = getDerived().RebuildPointerType( + PointeeType, TL.getPointerAttributes(), TL.getSigilLoc()); if (Result.isNull()) return QualType(); } @@ -7402,15 +7468,119 @@ QualType TreeTransform::TransformCountAttributedType( QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() || OldCount != NewCount) { - // Currently, CountAttributedType can only wrap incomplete array types. - Result = SemaRef.BuildCountAttributedArrayOrPointerType( - InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (SemaRef.getLangOpts().hasBoundsSafety()) { + Result = SemaRef.BuildCountAttributedType( + InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + } else { + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Currently, CountAttributedType can only wrap incomplete array types. + Result = SemaRef.BuildCountAttributedArrayOrPointerType( + InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + } } TLB.push(Result); return Result; } +/* TO_UPSTREAM(BoundsSafety) ON */ +template +QualType TreeTransform::TransformDynamicRangePointerType( + TypeLocBuilder &TLB, DynamicRangePointerTypeLoc TL) { + const DynamicRangePointerType *OldTy = TL.getTypePtr(); + QualType PointerTy = getDerived().TransformType(TLB, TL.getInnerLoc()); + if (PointerTy.isNull()) + return QualType(); + + Expr *OldStartPtr = TL.getStartPointer(); + Expr *NewStartPtr = nullptr; + if (OldStartPtr) { + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + nullptr, ExpressionKind::EK_AttrArgument); + + ExprResult StartPtrResult = getDerived().TransformExpr(OldStartPtr); + if (StartPtrResult.isInvalid()) + return QualType(); + NewStartPtr = StartPtrResult.get(); + } + + Expr *OldEndPtr = TL.getEndPointer(); + Expr *NewEndPtr = nullptr; + if (OldEndPtr) { + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + nullptr, ExpressionKind::EK_AttrArgument); + + ExprResult EndPtrResult = getDerived().TransformExpr(OldEndPtr); + if (EndPtrResult.isInvalid()) + return QualType(); + NewEndPtr = EndPtrResult.get(); + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || PointerTy != OldTy->desugar() || + OldStartPtr != NewStartPtr || OldEndPtr != NewEndPtr) { + Result = + SemaRef.BuildDynamicRangePointerType(PointerTy, NewStartPtr, NewEndPtr); + if (Result.isNull()) + return QualType(); + } + + TLB.push(Result); + return Result; +} + +template +QualType TreeTransform::TransformValueTerminatedType( + TypeLocBuilder &TLB, ValueTerminatedTypeLoc TL) { + const ValueTerminatedType *OldTy = TL.getTypePtr(); + QualType OriginalTy = getDerived().TransformType(TLB, TL.getOriginalLoc()); + if (OriginalTy.isNull() || + !(OriginalTy->isConstantArrayType() || + OriginalTy->isIncompleteArrayType() || OriginalTy->isPointerType()) || + OriginalTy->isValueTerminatedType()) { + return QualType(); + } + + Expr *OldTerminator = TL.getTerminatorExpr(); + Expr *NewTerminator = nullptr; + if (OldTerminator) { + ExprResult TransformRes = getDerived().TransformExpr(OldTerminator); + if (!TransformRes.get()) + return QualType(); + NewTerminator = TransformRes.get(); + + QualType PET; + if (const auto *PT = OriginalTy->getAs()) + PET = PT->getPointeeType(); + else + PET = SemaRef.Context.getAsArrayType(OriginalTy)->getElementType(); + + ExprResult CastRes(NewTerminator); + CastKind Kind = SemaRef.PrepareScalarCast(CastRes, PET); + CastRes = SemaRef.ImpCastExprToType(NewTerminator, PET, Kind); + if (!CastRes.get()) + return QualType(); + NewTerminator = CastRes.get(); + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || OriginalTy != OldTy->desugar() || + OldTerminator != NewTerminator) { + Result = SemaRef.Context.getValueTerminatedType(OriginalTy, NewTerminator); + } + + TLB.push(Result); + return Result; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + template QualType TreeTransform::TransformBTFTagAttributedType( TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) { @@ -12607,10 +12777,12 @@ TreeTransform::TransformCompoundLiteralExpr(CompoundLiteralExpr *E) { // Note: the expression type doesn't necessarily match the // type-as-written, but that's okay, because it should always be // derivable from the initializer. - + /* TO_UPSTREAM(BoundsSafety) ON */ return getDerived().RebuildCompoundLiteralExpr( E->getLParenLoc(), NewT, - /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get()); + /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get(), + E->isFileScope()); + /* TO_UPSTREAM(BoundsSafety) OFF */ } template @@ -15957,14 +16129,149 @@ TreeTransform::TransformAtomicExpr(AtomicExpr *E) { E->getOp(), E->getRParenLoc()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +template +ExprResult +TreeTransform::TransformBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + // Pointer promotion expressions, like implicit casts, are eliminated during + // transformation, since they will be recomputed by semantic analysis after + // transformation. + return getDerived().TransformExpr(E->getSubExprAsWritten()); +} + +template +ExprResult +TreeTransform::TransformAssumptionExpr(AssumptionExpr *E) { + // Assumption expressions, like implicit casts, are eliminated during + // transformation, since they will be recomputed by semantic analysis after + // transformation. + return getDerived().TransformExpr(E->getWrappedExpr()); +} + +template +ExprResult TreeTransform::TransformForgePtrExpr(ForgePtrExpr *E) { + ExprResult Addr = getDerived().TransformExpr(E->getAddr()); + if (Addr.isInvalid()) + return ExprError(); + + ExprResult Size = ExprEmpty(); + if (Expr *OldSize = E->getSize()) { + Size = getDerived().TransformExpr(OldSize); + if (Size.isInvalid()) + return ExprError(); + } + + if (Addr.get() == E->getAddr() && Size.get() == E->getSize()) + return E; + + return getDerived().RebuildForgePtrExpr(E->getBeginLoc(), E->getType(), + Addr.get(), Size.get(), + E->getEndLoc()); +} + +template +ExprResult TreeTransform::TransformGetBoundExpr(GetBoundExpr *E) { + ExprResult Addr = getDerived().TransformExpr(E->getSubExpr()); + if (Addr.isInvalid()) + return ExprError(); + + return getDerived().RebuildGetBoundExpr(E->getBeginLoc(), E->getEndLoc(), + Addr.get(), E->getBoundKind(), + !E->getType()->isPointerTypeWithBounds()); +} + +template +ExprResult TreeTransform::TransformPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + return getDerived().TransformExpr(E->getGuardedExpr()); +} + +template +ExprResult +TreeTransform::TransformBoundsCheckExpr(BoundsCheckExpr *E) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.insert(OVE); + } + + ExprResult NewGuarded = getDerived().TransformExpr(E->getGuardedExpr()); + + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.erase(OVE); + } + return NewGuarded; +} + +template +ExprResult +TreeTransform::TransformMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + if (E->isBinding()) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.insert(OVE); + } + } + ExprResult NewWrap = getDerived().TransformExpr(E->getWrappedExpr()); + + if (E->isUnbinding()) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.erase(OVE); + } + } + return NewWrap; +} + +template +ExprResult TreeTransform::TransformTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + ExprResult Pointer = getDerived().TransformExpr(E->getPointer()); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *Terminator = E->getTerminator(); + if (Terminator) { + ExprResult Res = getDerived().TransformExpr(Terminator); + if (Res.isInvalid()) + return ExprError(); + Terminator = Res.get(); + } + + return getDerived().RebuildTerminatedByToIndexableExpr( + E->getBeginLoc(), E->getEndLoc(), Pointer.get(), Terminator, + E->includesTerminator()); +} + +template +ExprResult TreeTransform::TransformTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + const auto *VTT = cast(E->getType()); + + ExprResult Pointer = getDerived().TransformExpr(E->getPointer()); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *PointerToTerminator = E->getPointerToTerminator(); + if (PointerToTerminator) { + ExprResult Res = getDerived().TransformExpr(PointerToTerminator); + if (Res.isInvalid()) + return ExprError(); + PointerToTerminator = Res.get(); + } + + return getDerived().RebuildTerminatedByFromIndexableExpr( + E->getBeginLoc(), E->getEndLoc(), VTT->getTerminatorExpr(), Pointer.get(), + PointerToTerminator); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + //===----------------------------------------------------------------------===// // Type reconstruction //===----------------------------------------------------------------------===// -template -QualType TreeTransform::RebuildPointerType(QualType PointeeType, - SourceLocation Star) { - return SemaRef.BuildPointerType(PointeeType, Star, +template +QualType +TreeTransform::RebuildPointerType(QualType PointeeType, + BoundsSafetyPointerAttributes Attr, + SourceLocation Star) { + return SemaRef.BuildPointerType(PointeeType, Attr, Star, getDerived().getBaseEntity()); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index d68cef0f7e5bb..eb5bed537a925 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3063,112 +3063,112 @@ ASTReader::ReadControlBlock(ModuleFile &F, break; } - case IMPORTS: { + case IMPORT: { // Validate the AST before processing any imports (otherwise, untangling // them can be error-prone and expensive). A module will have a name and // will already have been validated, but this catches the PCH case. if (ASTReadResult Result = readUnhashedControlBlockOnce()) return Result; - // Load each of the imported PCH files. - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. - ModuleKind ImportedKind = (ModuleKind)Record[Idx++]; - // Whether we're importing a standard c++ module. - bool IsImportingStdCXXModule = Record[Idx++]; - // The import location will be the local one for now; we will adjust - // all import locations of module imports after the global source - // location info are setup, in ReadAST. - auto [ImportLoc, ImportModuleFileIndex] = - ReadUntranslatedSourceLocation(Record[Idx++]); - // The import location must belong to the current module file itself. - assert(ImportModuleFileIndex == 0); - off_t StoredSize = !IsImportingStdCXXModule ? (off_t)Record[Idx++] : 0; - time_t StoredModTime = - !IsImportingStdCXXModule ? (time_t)Record[Idx++] : 0; - - ASTFileSignature StoredSignature; - if (!IsImportingStdCXXModule) { - auto FirstSignatureByte = Record.begin() + Idx; - StoredSignature = ASTFileSignature::create( - FirstSignatureByte, FirstSignatureByte + ASTFileSignature::size); - Idx += ASTFileSignature::size; - } + unsigned Idx = 0; + // Read information about the AST file. + ModuleKind ImportedKind = (ModuleKind)Record[Idx++]; + + // The import location will be the local one for now; we will adjust + // all import locations of module imports after the global source + // location info are setup, in ReadAST. + auto [ImportLoc, ImportModuleFileIndex] = + ReadUntranslatedSourceLocation(Record[Idx++]); + // The import location must belong to the current module file itself. + assert(ImportModuleFileIndex == 0); + + StringRef ImportedName = ReadStringBlob(Record, Idx, Blob); + + bool IsImportingStdCXXModule = Record[Idx++]; + + off_t StoredSize = 0; + time_t StoredModTime = 0; + ASTFileSignature StoredSignature; + std::string ImportedFile; + StringRef ImportedCacheKey; + + // For prebuilt and explicit modules first consult the file map for + // an override. Note that here we don't search prebuilt module + // directories if we're not importing standard c++ module, only the + // explicit name to file mappings. Also, we will still verify the + // size/signature making sure it is essentially the same file but + // perhaps in a different location. + if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) + ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName( + ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule); + + if (IsImportingStdCXXModule && ImportedFile.empty()) { + Diag(diag::err_failed_to_find_module_file) << ImportedName; + return Missing; + } - std::string ImportedName = ReadString(Record, Idx); - std::string ImportedFile; - std::string ImportedCacheKey; - - // For prebuilt and explicit modules first consult the file map for - // an override. Note that here we don't search prebuilt module - // directories if we're not importing standard c++ module, only the - // explicit name to file mappings. Also, we will still verify the - // size/signature making sure it is essentially the same file but - // perhaps in a different location. - if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) - ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName( - ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule); - - // For C++20 Modules, we won't record the path to the imported modules - // in the BMI - if (!IsImportingStdCXXModule) { - if (ImportedFile.empty()) { - // Use BaseDirectoryAsWritten to ensure we use the same path in the - // ModuleCache as when writing. - ImportedFile = ReadPath(BaseDirectoryAsWritten, Record, Idx); - ImportedCacheKey = ReadString(Record, Idx); - } else { - SkipPath(Record, Idx); - SkipString(Record, Idx); - } - } else if (ImportedFile.empty()) { - Diag(clang::diag::err_failed_to_find_module_file) << ImportedName; - return Missing; + if (!IsImportingStdCXXModule) { + StoredSize = (off_t)Record[Idx++]; + StoredModTime = (time_t)Record[Idx++]; + + StringRef SignatureBytes = Blob.substr(0, ASTFileSignature::size); + StoredSignature = ASTFileSignature::create(SignatureBytes.begin(), + SignatureBytes.end()); + Blob = Blob.substr(ASTFileSignature::size); + + if (ImportedFile.empty()) { + // Use BaseDirectoryAsWritten to ensure we use the same path in the + // ModuleCache as when writing. + ImportedFile = + ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob); + } else { + (void)ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob); } + ImportedCacheKey = ReadStringBlob(Record, Idx, Blob); + if (!ImportedCacheKey.empty()) { if (!Listener || Listener->readModuleCacheKey( ImportedName, ImportedFile, ImportedCacheKey)) { Diag(diag::err_ast_file_not_found) << moduleKindForDiagnostic(ImportedKind) << ImportedFile << true - << std::string("missing or unloadable module cache key") + - ImportedCacheKey; + << ("missing or unloadable module cache key" + + ImportedCacheKey).str(); return Failure; } } + } - // If our client can't cope with us being out of date, we can't cope with - // our dependency being missing. - unsigned Capabilities = ClientLoadCapabilities; - if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) - Capabilities &= ~ARR_Missing; - - // Load the AST file. - auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, - Loaded, StoredSize, StoredModTime, - StoredSignature, Capabilities); - - // If we diagnosed a problem, produce a backtrace. - bool recompilingFinalized = - Result == OutOfDate && (Capabilities & ARR_OutOfDate) && - getModuleManager().getModuleCache().isPCMFinal(F.FileName); - if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized) - Diag(diag::note_module_file_imported_by) - << F.FileName << !F.ModuleName.empty() << F.ModuleName; - if (recompilingFinalized) - Diag(diag::note_module_file_conflict); - - switch (Result) { - case Failure: return Failure; - // If we have to ignore the dependency, we'll have to ignore this too. - case Missing: - case OutOfDate: return OutOfDate; - case VersionMismatch: return VersionMismatch; - case ConfigurationMismatch: return ConfigurationMismatch; - case HadErrors: return HadErrors; - case Success: break; - } + // If our client can't cope with us being out of date, we can't cope with + // our dependency being missing. + unsigned Capabilities = ClientLoadCapabilities; + if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) + Capabilities &= ~ARR_Missing; + + // Load the AST file. + auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, + Loaded, StoredSize, StoredModTime, + StoredSignature, Capabilities); + + // If we diagnosed a problem, produce a backtrace. + bool recompilingFinalized = + Result == OutOfDate && (Capabilities & ARR_OutOfDate) && + getModuleManager().getModuleCache().isPCMFinal(F.FileName); + if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized) + Diag(diag::note_module_file_imported_by) + << F.FileName << !F.ModuleName.empty() << F.ModuleName; + if (recompilingFinalized) + Diag(diag::note_module_file_conflict); + + switch (Result) { + case Failure: return Failure; + // If we have to ignore the dependency, we'll have to ignore this too. + case Missing: + case OutOfDate: return OutOfDate; + case VersionMismatch: return VersionMismatch; + case ConfigurationMismatch: return ConfigurationMismatch; + case HadErrors: return HadErrors; + case Success: break; } break; } @@ -5611,39 +5611,41 @@ bool ASTReader::readASTFileControlBlock( break; } - case IMPORTS: { + case IMPORT: { if (!NeedsImports) break; - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. + unsigned Idx = 0; + // Read information about the AST file. - // Skip Kind - Idx++; - bool IsStandardCXXModule = Record[Idx++]; + // Skip Kind + Idx++; - // Skip ImportLoc - Idx++; + // Skip ImportLoc + Idx++; - // In C++20 Modules, we don't record the path to imported - // modules in the BMI files. - if (IsStandardCXXModule) { - std::string ModuleName = ReadString(Record, Idx); - Listener.visitImport(ModuleName, /*Filename=*/""); - continue; - } + StringRef ModuleName = ReadStringBlob(Record, Idx, Blob); - // Skip Size, ModTime and Signature - Idx += 1 + 1 + ASTFileSignature::size; - std::string ModuleName = ReadString(Record, Idx); - std::string FilenameStr = ReadString(Record, Idx); - auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir); - std::string CacheKey = ReadString(Record, Idx); - if (!CacheKey.empty()) - Listener.readModuleCacheKey(ModuleName, *Filename, CacheKey); - Listener.visitImport(ModuleName, *Filename); + bool IsStandardCXXModule = Record[Idx++]; + + // In C++20 Modules, we don't record the path to imported + // modules in the BMI files. + if (IsStandardCXXModule) { + Listener.visitImport(ModuleName, /*Filename=*/""); + continue; } + + // Skip Size and ModTime. + Idx += 1 + 1; + // Skip signature. + Blob = Blob.substr(ASTFileSignature::size); + + StringRef FilenameStr = ReadStringBlob(Record, Idx, Blob); + auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir); + StringRef CacheKey = ReadStringBlob(Record, Idx, Blob); + if (!CacheKey.empty()) + Listener.readModuleCacheKey(ModuleName, *Filename, CacheKey); + Listener.visitImport(ModuleName, *Filename); break; } @@ -7033,6 +7035,17 @@ void TypeLocReader::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) { // Nothing to do } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypeLocReader::VisitDynamicRangePointerTypeLoc( + DynamicRangePointerTypeLoc TL) { + // nothing to do +} + +void TypeLocReader::VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + // nothing to do +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } @@ -9492,6 +9505,14 @@ std::string ASTReader::ReadString(const RecordDataImpl &Record, unsigned &Idx) { return Result; } +StringRef ASTReader::ReadStringBlob(const RecordDataImpl &Record, unsigned &Idx, + StringRef &Blob) { + unsigned Len = Record[Idx++]; + StringRef Result = Blob.substr(0, Len); + Blob = Blob.substr(Len); + return Result; +} + std::string ASTReader::ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx) { return ReadPath(F.BaseDirectory, Record, Idx); @@ -9503,6 +9524,13 @@ std::string ASTReader::ReadPath(StringRef BaseDirectory, return ResolveImportedPathAndAllocate(PathBuf, Filename, BaseDirectory); } +std::string ASTReader::ReadPathBlob(StringRef BaseDirectory, + const RecordData &Record, unsigned &Idx, + StringRef &Blob) { + StringRef Filename = ReadStringBlob(Record, Idx, Blob); + return ResolveImportedPathAndAllocate(PathBuf, Filename, BaseDirectory); +} + VersionTuple ASTReader::ReadVersionTuple(const RecordData &Record, unsigned &Idx) { unsigned Major = Record[Idx++]; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f6af4aaf4bcf6..0932b0863a222 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2377,6 +2377,101 @@ void ASTStmtReader::VisitAsTypeExpr(AsTypeExpr *E) { E->SrcExpr = Record.readSubExpr(); } +//===----------------------------------------------------------------------===// +// BoundsSafety Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtReader::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E) { + VisitExpr(E); + switch (Record.readInt()) { + case 3: E->setLowerBound(Record.readSubExpr()); [[clang::fallthrough]]; + case 2: E->setUpperBound(Record.readSubExpr()); [[clang::fallthrough]]; + case 1: E->setPointer(Record.readSubExpr()); break; + default: llvm_unreachable("incorrect number of child nodes"); + } +} + +void ASTStmtReader::VisitAssumptionExpr(AssumptionExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); + assert(NumExprs == E->AssumptionExprBits.NumExprs); + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitForgePtrExpr(ForgePtrExpr *E) { + VisitExpr(E); + E->setAddr(Record.readSubExpr()); + E->setSize(Record.readSubExpr()); + E->setBeginLoc(readSourceLocation()); + E->setEndLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitGetBoundExpr(GetBoundExpr *E) { + VisitExpr(E); + E->setSubExpr(Record.readSubExpr()); + E->setBoundKind((GetBoundExpr::BoundKind)Record.readInt()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); +#ifndef NDEBUG + BoundsCheckKind Kind = static_cast(Record.readInt()); + assert(Kind == E->getKind() && NumExprs == E->getNumSubExprs()); +#endif + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitBoundsCheckExpr(BoundsCheckExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); + assert(NumExprs == E->BoundsCheckExprBits.NumChildren); + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + VisitExpr(E); + unsigned numExprs = Record.readInt(); + assert(numExprs == E->MaterializeSequenceExprBits.NumExprs); + E->MaterializeSequenceExprBits.Unbind = Record.readInt(); + + for (unsigned i = 0; i != numExprs; ++i) { + E->getSubExprs()[i] = Record.readSubExpr(); + } +} + +void ASTStmtReader::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + VisitExpr(E); + E->setPointer(Record.readSubExpr()); + bool HasTerm = Record.readInt(); + if (HasTerm) + E->setTerminator(Record.readSubExpr()); + E->setIncludeTerminator(Record.readInt()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + VisitExpr(E); + E->setPointer(Record.readSubExpr()); + bool HasPtrToTerm = Record.readInt(); + if (HasPtrToTerm) + E->setPointerToTerminator(Record.readSubExpr()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + //===----------------------------------------------------------------------===// // OpenMP Directives. //===----------------------------------------------------------------------===// @@ -4002,6 +4097,48 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; } + case EXPR_BOUNDS_SAFETY_POINTER_PROMOTION: + S = BoundsSafetyPointerPromotionExpr::CreateEmpty( + Context, Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_ASSUMPTION: + S = AssumptionExpr::CreateEmpty(Context, + Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_FORGE_PTR: + S = new (Context) ForgePtrExpr(Empty); + break; + + case EXPR_GET_BOUND: + S = new (Context) GetBoundExpr(Empty); + break; + + case EXPR_PREDEFINED_BOUNDS_CHECK: + S = PredefinedBoundsCheckExpr::CreateEmpty( + Context, + /*NumChildren*/ Record[ASTStmtReader::NumExprFields + 1]); + break; + + case EXPR_BOUNDS_CHECK: + S = BoundsCheckExpr::CreateEmpty(Context, + /*NumChildren*/Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_MATERIALIZE_SEQUENCE: + S = MaterializeSequenceExpr::CreateEmpty(Context, + /*NumExprs*/Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_TERMINATED_BY_TO_INDEXABLE: + S = new (Context) TerminatedByToIndexableExpr(Empty); + break; + + case EXPR_TERMINATED_BY_FROM_INDEXABLE: + S = new (Context) TerminatedByFromIndexableExpr(Empty); + break; + case EXPR_USER_DEFINED_LITERAL: { auto NumArgs = Record[ASTStmtReader::NumExprFields]; BitsUnpacker CallExprBits(Record[ASTStmtReader::NumExprFields + 1]); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index d693a767d998f..ac5dcd4bfd60d 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -555,6 +555,17 @@ void TypeLocWriter::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) { // Nothing to do } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypeLocWriter::VisitDynamicRangePointerTypeLoc( + DynamicRangePointerTypeLoc TL) { + // nothing to do +} + +void TypeLocWriter::VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + // nothing to do +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } @@ -854,7 +865,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(MODULE_NAME); RECORD(MODULE_DIRECTORY); RECORD(MODULE_MAP_FILE); - RECORD(IMPORTS); + RECORD(IMPORT); RECORD(ORIGINAL_FILE); RECORD(ORIGINAL_FILE_ID); RECORD(INPUT_FILE_OFFSETS); @@ -1551,38 +1562,57 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { // Imports if (Chain) { - serialization::ModuleManager &Mgr = Chain->getModuleManager(); - Record.clear(); + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(IMPORT)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ImportLoc + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Standard C++ mod + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File timestamp + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File name len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Cache key len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Strings + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); - for (ModuleFile &M : Mgr) { + SmallString<128> Blob; + + for (ModuleFile &M : Chain->getModuleManager()) { // Skip modules that weren't directly imported. if (!M.isDirectlyImported()) continue; + Record.clear(); + Blob.clear(); + + Record.push_back(IMPORT); Record.push_back((unsigned)M.Kind); // FIXME: Stable encoding - Record.push_back(M.StandardCXXModule); AddSourceLocation(M.ImportLoc, Record); + AddStringBlob(M.ModuleName, Record, Blob); + Record.push_back(M.StandardCXXModule); // We don't want to hard code the information about imported modules // in the C++20 named modules. - if (!M.StandardCXXModule) { + if (M.StandardCXXModule) { + Record.push_back(0); + Record.push_back(0); + Record.push_back(0); + // FIXME: cache support for standard C++ modules. + Record.push_back(0); + } else { // If we have calculated signature, there is no need to store // the size or timestamp. Record.push_back(M.Signature ? 0 : M.File.getSize()); Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.File)); - llvm::append_range(Record, M.Signature); - } - AddString(M.ModuleName, Record); + llvm::append_range(Blob, M.Signature); - if (!M.StandardCXXModule) { - AddPath(M.FileName, Record); - // FIXME: cache support for standard C++ modules. - AddString(M.ModuleCacheKey, Record); + AddPathBlob(M.FileName, Record, Blob); + AddStringBlob(M.ModuleCacheKey, Record, Blob); } + Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob); } - Stream.EmitRecord(IMPORTS, Record); } // Write the options block. @@ -4744,6 +4774,12 @@ void ASTWriter::AddString(StringRef Str, RecordDataImpl &Record) { Record.insert(Record.end(), Str.begin(), Str.end()); } +void ASTWriter::AddStringBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl &Blob) { + Record.push_back(Str.size()); + Blob.insert(Blob.end(), Str.begin(), Str.end()); +} + bool ASTWriter::PreparePathForOutput(SmallVectorImpl &Path) { assert(WritingAST && "can't prepare path for output when not writing AST"); @@ -4772,6 +4808,13 @@ void ASTWriter::AddPath(StringRef Path, RecordDataImpl &Record) { AddString(FilePath, Record); } +void ASTWriter::AddPathBlob(StringRef Path, RecordDataImpl &Record, + SmallVectorImpl &Blob) { + SmallString<128> FilePath(Path); + PreparePathForOutput(FilePath); + AddStringBlob(FilePath, Record, Blob); +} + void ASTWriter::EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, StringRef Path) { SmallString<128> FilePath(Path); @@ -7039,6 +7082,20 @@ void ASTWriter::AddedAttributeToRecord(const Attr *Attr, DeclUpdates[Record].push_back(DeclUpdate(UPD_ADDED_ATTR_TO_RECORD, Attr)); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +// This function uses DeclUpdates to store the attribute in a separate record, +// so as to avoid deserialization cycle between the attribute and the Decl. +// Thus, this will be called during 'WritingAST' and should cover Decl either +// from parsing or an AST file. +void ASTWriter::LazyAttributeToDecl(const Attr *Attr, + const Decl *D) { + if (Chain && Chain->isProcessingUpdateRecords()) return; + // Reuse UPD_ADDED_ATTR_TO_RECORD to share its functionality to write and + // read the attribute for Decl. + DeclUpdates[D].push_back(DeclUpdate(UPD_ADDED_ATTR_TO_RECORD, Attr)); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void ASTWriter::AddedCXXTemplateSpecialization( const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) { assert(!WritingAST && "Already writing the AST!"); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 57289c27223ec..41391cd0821d6 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -358,6 +358,19 @@ void ASTDeclWriter::Visit(Decl *D) { } void ASTDeclWriter::VisitDecl(Decl *D) { + /// BoundsSafety: Workaround to avoid a cycle during deserialization. + /// \code + /// int len; + /// int *__counted_by(len) ptr; + /// \endcode + /// In the above example, DependerDeclsAttr referencing 'ptr' is attached + /// to 'len' as a Decl attribute. To workaround the issue, we serialize + /// DependerDeclsAttr separately, creating a map between Decl and Attribute. + if (DependerDeclsAttr *DDA = D->getAttr()) { + Writer.LazyAttributeToDecl(DDA, D); + D->dropAttr(); + } + BitsPacker DeclBits; // The order matters here. It will be better to put the bit with higher diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 6e9b14a2c497f..0b6aaf0513355 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2312,6 +2312,106 @@ void ASTStmtWriter::VisitAsTypeExpr(AsTypeExpr *E) { Code = serialization::EXPR_ASTYPE; } +//===----------------------------------------------------------------------===// +// BoundsSafety Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtWriter::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumChildren()); + switch (E->getNumChildren()) { + case 3: Record.AddStmt(E->getLowerBound()); [[clang::fallthrough]]; + case 2: Record.AddStmt(E->getUpperBound()); [[clang::fallthrough]]; + case 1: Record.AddStmt(E->getPointer()); break; + default: llvm_unreachable("incorrect number of child nodes"); + } + Code = serialization::EXPR_BOUNDS_SAFETY_POINTER_PROMOTION; +} + +void ASTStmtWriter::VisitAssumptionExpr(AssumptionExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_ASSUMPTION; +} + +void ASTStmtWriter::VisitForgePtrExpr(ForgePtrExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getAddr()); + Record.AddStmt(E->getSize()); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); + Code = serialization::EXPR_FORGE_PTR; +} + +void ASTStmtWriter::VisitGetBoundExpr(GetBoundExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getSubExpr()); + Record.push_back((int)E->getBoundKind()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_GET_BOUND; +} + +void ASTStmtWriter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + VisitExpr(E); + Record.push_back(static_cast(E->getKind())); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_PREDEFINED_BOUNDS_CHECK; +} + +void ASTStmtWriter::VisitBoundsCheckExpr(BoundsCheckExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_BOUNDS_CHECK; +} + +void ASTStmtWriter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + Record.push_back(E->isUnbinding()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_MATERIALIZE_SEQUENCE; +} + +void ASTStmtWriter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getPointer()); + bool HasTerm = E->getTerminator() != nullptr; + Record.push_back(HasTerm); + if (HasTerm) + Record.AddStmt(E->getTerminator()); + Record.push_back(E->includesTerminator()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_TERMINATED_BY_TO_INDEXABLE; +} + +void ASTStmtWriter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getPointer()); + bool HasPtrToTerm = E->getPointerToTerminator() != nullptr; + Record.push_back(HasPtrToTerm); + if (HasPtrToTerm) + Record.AddStmt(E->getPointerToTerminator()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_TERMINATED_BY_FROM_INDEXABLE; +} + //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements. //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 94b8f17b6a61c..be9fb2b2cc9b1 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -615,65 +615,58 @@ llvm::Error GlobalModuleIndexBuilder::loadModuleFile(FileEntryRef File) { unsigned Code = MaybeCode.get(); // Handle module dependencies. - if (State == ControlBlock && Code == IMPORTS) { - // Load each of the imported PCH files. - unsigned Idx = 0, N = Record.size(); - while (Idx < N) { - // Read information about the AST file. - - // Skip the imported kind - ++Idx; - - // Skip if it is standard C++ module - ++Idx; - - // Skip the import location - ++Idx; - - // Load stored size/modification time. - off_t StoredSize = (off_t)Record[Idx++]; - time_t StoredModTime = (time_t)Record[Idx++]; - - // Skip the stored signature. - // FIXME: we could read the signature out of the import and validate it. - auto FirstSignatureByte = Record.begin() + Idx; - ASTFileSignature StoredSignature = ASTFileSignature::create( - FirstSignatureByte, FirstSignatureByte + ASTFileSignature::size); - Idx += ASTFileSignature::size; - - // Skip the module name (currently this is only used for prebuilt - // modules while here we are only dealing with cached). - Idx += Record[Idx] + 1; - - // Retrieve the imported file name. - unsigned Length = Record[Idx++]; - SmallString<128> ImportedFile(Record.begin() + Idx, - Record.begin() + Idx + Length); - Idx += Length; - - // Skip the module cache key. - Idx += Record[Idx] + 1; - - // Find the imported module file. - auto DependsOnFile = - FileMgr.getOptionalFileRef(ImportedFile, /*OpenFile=*/false, - /*CacheFailure=*/false); - - if (!DependsOnFile) - return llvm::createStringError(std::errc::bad_file_descriptor, - "imported file \"%s\" not found", - ImportedFile.c_str()); - - // Save the information in ImportedModuleFileInfo so we can verify after - // loading all pcms. - ImportedModuleFiles.insert(std::make_pair( - *DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime, - StoredSignature))); - - // Record the dependency. - unsigned DependsOnID = getModuleFileInfo(*DependsOnFile).ID; - getModuleFileInfo(File).Dependencies.push_back(DependsOnID); - } + if (State == ControlBlock && Code == IMPORT) { + unsigned Idx = 0; + // Read information about the AST file. + + // Skip the imported kind + ++Idx; + + // Skip the import location + ++Idx; + + // Skip the module name (currently this is only used for prebuilt + // modules while here we are only dealing with cached). + Blob = Blob.substr(Record[Idx++]); + + // Skip if it is standard C++ module + ++Idx; + + // Load stored size/modification time. + off_t StoredSize = (off_t)Record[Idx++]; + time_t StoredModTime = (time_t)Record[Idx++]; + + // Skip the stored signature. + // FIXME: we could read the signature out of the import and validate it. + StringRef SignatureBytes = Blob.substr(0, ASTFileSignature::size); + auto StoredSignature = ASTFileSignature::create(SignatureBytes.begin(), + SignatureBytes.end()); + Blob = Blob.substr(ASTFileSignature::size); + + // Retrieve the imported file name. + unsigned Length = Record[Idx++]; + StringRef ImportedFile = Blob.substr(0, Length); + Blob = Blob.substr(Length); + + // Find the imported module file. + auto DependsOnFile = + FileMgr.getOptionalFileRef(ImportedFile, /*OpenFile=*/false, + /*CacheFailure=*/false); + + if (!DependsOnFile) + return llvm::createStringError(std::errc::bad_file_descriptor, + "imported file \"%s\" not found", + std::string(ImportedFile).c_str()); + + // Save the information in ImportedModuleFileInfo so we can verify after + // loading all pcms. + ImportedModuleFiles.insert(std::make_pair( + *DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime, + StoredSignature))); + + // Record the dependency. + unsigned DependsOnID = getModuleFileInfo(*DependsOnFile).ID; + getModuleFileInfo(File).Dependencies.push_back(DependsOnID); continue; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index c11468a08ae5c..8719faf373955 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1719,6 +1719,16 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, assert(!isa(S) || S == cast(S)->IgnoreParens()); switch (S->getStmtClass()) { + // BoundsSafety expressions are not supported yet. + case Stmt::AssumptionExprClass: + case Stmt::BoundsSafetyPointerPromotionExprClass: + case Stmt::ForgePtrExprClass: + case Stmt::GetBoundExprClass: + case Stmt::PredefinedBoundsCheckExprClass: + case Stmt::BoundsCheckExprClass: + case Stmt::MaterializeSequenceExprClass: + case Stmt::TerminatedByToIndexableExprClass: + case Stmt::TerminatedByFromIndexableExprClass: // C++, OpenMP and ARC stuff we don't support yet. case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXTryStmtClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 7a900780384a9..343ad85e989f1 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -533,6 +533,16 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BoundsSafetyPointerCast: { + SVal V = state->getSVal(Ex, LCtx); + SVal CastedV = + svalBuilder.evalCast(V, CastE->getType(), Ex->getType()); + state = state->BindExpr(CastE, LCtx, CastedV); + Bldr.generateNode(CastE, Pred, state); + continue; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } } } diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index eb9cde5c8918f..f82e1fbee52f0 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -629,6 +629,16 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, // `evalCast` and its helper `EvalCastVisitor` //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool isConcretePointerWidthChangingCast(ASTContext &ACtx, SVal V, + QualType CastTy, + QualType OriginalTy) { + return V.getAs() && + CastTy->isPointerType() && OriginalTy->isPointerType() && + ACtx.getTypeSize(CastTy) != ACtx.getTypeSize(OriginalTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + namespace { class EvalCastVisitor : public SValVisitor { private: @@ -657,7 +667,8 @@ class EvalCastVisitor : public SValVisitor { // FIXME: Move this check to the most appropriate // evalCastKind/evalCastSubKind function. For const casts, casts to void, // just propagate the value. - if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType()) + if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType() && + !isConcretePointerWidthChangingCast(Context, V, CastTy, OriginalTy)) if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy), Context.getPointerType(OriginalTy))) return V; diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes new file mode 100644 index 0000000000000..5a3d628b66b6c --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes @@ -0,0 +1,135 @@ +--- +Name: BoundsUnsafe +Functions: + - Name: asdf_counted + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Name: asdf_sized + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by + Level: 0 + BoundedBy: size + - Name: asdf_counted_n + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by_or_null + Level: 0 + BoundedBy: len + - Name: asdf_sized_n + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by_or_null + Level: 0 + BoundedBy: size + - Name: asdf_ended + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Name: asdf_sized_mul + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by + Level: 0 + BoundedBy: "size * count" + - Name: asdf_counted_out + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 1 + BoundedBy: "*len" + - Name: asdf_counted_const + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: 7 + - Name: asdf_counted_nullable + Parameters: + - Position: 1 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Name: asdf_counted_noescape + Parameters: + - Position: 0 + NoEscape: true + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Name: asdf_counted_default_level + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + BoundedBy: len + - Name: asdf_counted_redundant + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + BoundedBy: len + - Name: asdf_ended_chained + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: mid + - Position: 1 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Name: asdf_ended_chained_reverse + Parameters: + - Position: 1 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: mid + - Name: asdf_ended_already_started + Parameters: + - Position: 1 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Name: asdf_ended_already_ended + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: mid + - Name: asdf_ended_redundant + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Name: asdf_nterm + Parameters: + - Position: 0 + Type: "int * __attribute__((__terminated_by__(0)))" diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h new file mode 100644 index 0000000000000..82a1ca05475d9 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h @@ -0,0 +1,21 @@ +void asdf_counted(int * buf, int len); +void asdf_sized(int * buf, int size); +void asdf_counted_n(int * buf, int len); +void asdf_sized_n(int * buf, int size); +void asdf_ended(int * buf, int * end); + +void asdf_sized_mul(int * buf, int size, int count); +void asdf_counted_out(int ** buf, int * len); +void asdf_counted_const(int * buf); +void asdf_counted_nullable(int len, int * _Nullable buf); +void asdf_counted_noescape(int * buf, int len); +void asdf_counted_default_level(int * buf, int len); +void asdf_counted_redundant(int * __attribute__((__counted_by__(len))) buf, int len); + +void asdf_ended_chained(int * buf, int * mid, int * end); +void asdf_ended_chained_reverse(int * buf, int * mid, int * end); +void asdf_ended_already_started(int * __attribute__((__ended_by__(mid))) buf, int * mid, int * end); +void asdf_ended_already_ended(int * buf, int * mid __attribute__((__ended_by__(end))), int * end); +void asdf_ended_redundant(int * __attribute__((__ended_by__(end))) buf, int * end); + +void asdf_nterm(char * buf); diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.apinotes b/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.apinotes new file mode 100644 index 0000000000000..d2e41dd4253e2 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.apinotes @@ -0,0 +1,92 @@ +--- +Name: BoundsUnsafe +Classes: + - Name: Foo + Methods: + - Selector: "asdf_counted:len:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Selector: "asdf_sized:size:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by + Level: 0 + BoundedBy: size + - Selector: "asdf_counted_n:len:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by_or_null + Level: 0 + BoundedBy: len + - Selector: "asdf_sized_n:size:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by_or_null + Level: 0 + BoundedBy: size + - Selector: "asdf_ended:end:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: end + - Selector: "asdf_sized_mul:size:count:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: sized_by + Level: 0 + BoundedBy: "size * count" + - Selector: "asdf_counted_out:len:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 1 + BoundedBy: "*len" + - Selector: "asdf_counted_const:" + MethodKind: Instance + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: 7 + - Selector: "asdf_counted_nullable:buf:" + MethodKind: Instance + Parameters: + - Position: 1 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Selector: "asdf_counted_noescape:len:" + MethodKind: Instance + Parameters: + - Position: 0 + NoEscape: true + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Selector: "asdf_nterm:" + MethodKind: Instance + Parameters: + - Position: 0 + Type: "int * __attribute__((__terminated_by__(0)))" + diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.h b/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.h new file mode 100644 index 0000000000000..111472f51b5a7 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafeObjC.h @@ -0,0 +1,16 @@ +@interface Foo +- (void) asdf_counted: (int *)buf len: (int)len; +- (void) asdf_sized: (int *)buf size: (int)size; +- (void) asdf_counted_n: (int *)buf len: (int)len; +- (void) asdf_sized_n: (int *)buf size: (int)size; +- (void) asdf_ended: (int *)buf end: (int *)end; + +- (void) asdf_sized_mul: (int *)buf size:(int)size count:(int)count; +- (void) asdf_counted_out: (int **)buf len:(int *)len; +- (void) asdf_counted_const: (int *)buf; +- (void) asdf_counted_nullable: (int)len buf:(int * _Nullable)buf; +- (void) asdf_counted_noescape: (int *)buf len: (int)len; + +- (void) asdf_nterm: (char *) buf; +@end + diff --git a/clang/test/APINotes/Inputs/Headers/Templates.h b/clang/test/APINotes/Inputs/Headers/Templates.h index 862035fee363f..2a86a46d4af27 100644 --- a/clang/test/APINotes/Inputs/Headers/Templates.h +++ b/clang/test/APINotes/Inputs/Headers/Templates.h @@ -6,4 +6,5 @@ struct Box { const T* get_ptr() const { return &value; } }; +using FloatBox = Box; using IntBox = Box; diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 31f7d36356d83..b6fa0fe6f6ab2 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -1,3 +1,15 @@ +module BoundsUnsafe { + header "BoundsUnsafe.h" +} + +module BoundsUnsafeErrors { + header "BoundsUnsafeErrors.h" +} + +module BoundsUnsafeObjC { + header "BoundsUnsafeObjC.h" +} + module ExternCtx { header "ExternCtx.h" } diff --git a/clang/test/APINotes/boundssafety-errors.c b/clang/test/APINotes/boundssafety-errors.c new file mode 100644 index 0000000000000..72dcbfa622931 --- /dev/null +++ b/clang/test/APINotes/boundssafety-errors.c @@ -0,0 +1,135 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t/Headers +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fsyntax-only -fapinotes-modules -I %t/Headers -fexperimental-bounds-safety-attributes %t/Headers/SemaErrors.c 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fsyntax-only -fapinotes-modules -I %t/Headers -fexperimental-bounds-safety-attributes %t/Headers/NegLevel.c 2>&1 | FileCheck %s --check-prefix NEGLEVEL +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fsyntax-only -fapinotes-modules -I %t/Headers -fexperimental-bounds-safety-attributes %t/Headers/InvalidKind.c 2>&1 | FileCheck %s --check-prefix INVALIDKIND + +//--- module.modulemap +module SemaErrors { + header "SemaErrors.h" +} +module NegLevel { + header "NegLevel.h" +} +module InvalidKind { + header "InvalidKind.h" +} + +//--- SemaErrors.c +#include "SemaErrors.h" +//--- SemaErrors.apinotes +Name: OOBLevel +Functions: + - Name: oob_level + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 42 + BoundedBy: len + - Name: off_by_1_level + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 1 + BoundedBy: len + - Name: nonpointer_param + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Name: wrong_name + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: len + - Name: wrong_scope + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: static_len + - Name: mismatching_count + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: 0 + BoundedBy: apinote_len + - Name: mismatching_end + Parameters: + - Position: 0 + BoundsSafety: + Kind: ended_by + Level: 0 + BoundedBy: apinote_end +//--- SemaErrors.h +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: __counted_by attribute only applies to pointer arguments +// CHECK-NEXT: oob_level +void oob_level(int * buf, int len); +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: __counted_by attribute only applies to pointer arguments +// CHECK-NEXT: off_by_1_level +void off_by_1_level(int * buf, int len); +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: __counted_by attribute only applies to pointer arguments +// CHECK-NEXT: nonpointer_param +void nonpointer_param(int buf, int len); +// CHECK: :1:1: error: use of undeclared identifier 'len'; did you mean 'len2'? +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: note: 'len2' declared here +// CHECK-NEXT: wrong_name +void wrong_name(int * buf, int len2); +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: count expression in function declaration may only reference parameters of that function +// CHECK-NEXT: wrong_scope +int static_len = 5; +void wrong_scope(int * buf); +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: pointer cannot have more than one count attribute +// CHECK-NEXT: mismatching_count +void mismatching_count(int * __attribute__((__counted_by__(header_len))) buf, int apinote_len, int header_len); +// CHECK: SemaErrors.h:{{.*}}:{{.*}}: error: pointer cannot have more than one end attribute +// CHECK-NEXT: mismatching_end +void mismatching_end(int * __attribute__((__ended_by__(header_end))) buf, int * apinote_end, int * header_end); +// CHECK: SemaErrors.c:{{.*}}:{{.*}}: fatal error: could not build module 'SemaErrors' + + +//--- NegLevel.apinotes +Name: NegLevel +Functions: + - Name: neg_level + Parameters: + - Position: 0 + BoundsSafety: + Kind: counted_by + Level: -1 + BoundedBy: len +//--- NegLevel.h +void neg_level(int * buf, int len); +//--- NegLevel.c +#include "NegLevel.h" +// NEGLEVEL: NegLevel.apinotes:{{.*}}:{{.*}}: error: invalid number +// NEGLEVEL-NEXT: Level: -1 +// NEGLEVEL: NegLevel.c:{{.*}}:{{.*}}: fatal error: could not build module 'NegLevel' + + +//--- InvalidKind.apinotes +Name: InvalidKind +Functions: + - Name: invalid_kind + Parameters: + - Position: 0 + BoundsSafety: + Kind: __counted_by + Level: 0 + BoundedBy: len +//--- InvalidKind.h +void invalid_kind(int * buf, int len); +//--- InvalidKind.c +#include "InvalidKind.h" +// INVALIDKIND: InvalidKind.apinotes:{{.*}}:{{.*}}: error: unknown enumerated scalar +// INVALIDKIND-NEXT: Kind: __counted_by +// INVALIDKIND: InvalidKind.c:{{.*}}:{{.*}}: fatal error: could not build module 'InvalidKind' + diff --git a/clang/test/APINotes/boundssafety.c b/clang/test/APINotes/boundssafety.c new file mode 100644 index 0000000000000..3140d88dfcaeb --- /dev/null +++ b/clang/test/APINotes/boundssafety.c @@ -0,0 +1,69 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fsyntax-only -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks -fexperimental-bounds-safety-attributes %s -ast-dump -ast-dump-filter asdf | FileCheck %s + +#include "BoundsUnsafe.h" + +// CHECK: asdf_counted 'void (int * __counted_by(len), int)' +// CHECK: buf 'int * __counted_by(len)':'int *' + +// CHECK: asdf_sized 'void (int * __sized_by(size), int)' +// CHECK: buf 'int * __sized_by(size)':'int *' + +// CHECK: asdf_counted_n 'void (int * __counted_by_or_null(len), int)' +// CHECK: buf 'int * __counted_by_or_null(len)':'int *' + +// CHECK: asdf_sized_n 'void (int * __sized_by_or_null(size), int)' +// CHECK: buf 'int * __sized_by_or_null(size)':'int *' + +// CHECK: asdf_ended 'void (int * __ended_by(end), int * /* __started_by(buf) */ )' +// CHECK: buf 'int * __ended_by(end)':'int *' +// CHECK: end 'int * /* __started_by(buf) */ ':'int *' + +// CHECK: asdf_sized_mul 'void (int * __sized_by(size * count), int, int)' +// CHECK: buf 'int * __sized_by(size * count)':'int *' + +// CHECK: asdf_counted_out 'void (int * __counted_by(*len)*, int *)' +// CHECK: buf 'int * __counted_by(*len)*' + +// CHECK: asdf_counted_const 'void (int * __counted_by(7))' +// CHECK: buf 'int * __counted_by(7)':'int *' + +// CHECK: asdf_counted_nullable 'void (int, int * __counted_by(len) _Nullable)' +// CHECK: buf 'int * __counted_by(len) _Nullable':'int *' + +// CHECK: asdf_counted_noescape 'void (int * __counted_by(len), int)' +// CHECK: buf 'int * __counted_by(len)':'int *' +// CHECK-NEXT: NoEscapeAttr + +// CHECK: asdf_counted_default_level 'void (int * __counted_by(len), int)' +// CHECK: buf 'int * __counted_by(len)':'int *' + +// CHECK: asdf_counted_redundant 'void (int * __counted_by(len), int)' +// CHECK: buf 'int * __counted_by(len)':'int *' + +// CHECK: asdf_ended_chained 'void (int * __ended_by(mid), int * __ended_by(end) /* __started_by(buf) */ , int * /* __started_by(mid) */ )' +// CHECK: buf 'int * __ended_by(mid)':'int *' +// CHECK: mid 'int * __ended_by(end) /* __started_by(buf) */ ':'int *' +// CHECK: end 'int * /* __started_by(mid) */ ':'int *' + +// CHECK: asdf_ended_chained_reverse 'void (int * __ended_by(mid), int * __ended_by(end) /* __started_by(buf) */ , int * /* __started_by(mid) */ )' +// CHECK: buf 'int * __ended_by(mid)':'int *' +// CHECK: mid 'int * __ended_by(end) /* __started_by(buf) */ ':'int *' +// CHECK: end 'int * /* __started_by(mid) */ ':'int *' + +// CHECK: asdf_ended_already_started 'void (int * __ended_by(mid), int * __ended_by(end) /* __started_by(buf) */ , int * /* __started_by(mid) */ )' +// CHECK: buf 'int * __ended_by(mid)':'int *' +// CHECK: mid 'int * __ended_by(end) /* __started_by(buf) */ ':'int *' +// CHECK: end 'int * /* __started_by(mid) */ ':'int *' + +// CHECK: asdf_ended_already_ended 'void (int * __ended_by(mid), int * __ended_by(end) /* __started_by(buf) */ , int * /* __started_by(mid) */ )' +// CHECK: buf 'int * __ended_by(mid)':'int *' +// CHECK: mid 'int * __ended_by(end) /* __started_by(buf) */ ':'int *' +// CHECK: end 'int * /* __started_by(mid) */ ':'int *' + +// CHECK: asdf_ended_redundant 'void (int * __ended_by(end), int * /* __started_by(buf) */ )' +// CHECK: buf 'int * __ended_by(end)':'int *' +// CHECK: end 'int * /* __started_by(buf) */ ':'int *' + +// CHECK: asdf_nterm 'void (int * __terminated_by(0))' +// CHECK: buf 'int * __terminated_by(0)':'int *' diff --git a/clang/test/APINotes/boundssafety.m b/clang/test/APINotes/boundssafety.m new file mode 100644 index 0000000000000..19e14607c2969 --- /dev/null +++ b/clang/test/APINotes/boundssafety.m @@ -0,0 +1,55 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fsyntax-only -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks -fexperimental-bounds-safety-attributes %s -ast-dump -ast-dump-filter asdf | FileCheck %s + +#include "BoundsUnsafeObjC.h" + +// CHECK-LABEL: asdf_counted +// CHECK: buf 'int * __counted_by(len)':'int *' +// CHECK-NEXT: len 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 + +// CHECK-LABEL: asdf_sized +// CHECK: buf 'int * __sized_by(size)':'int *' +// CHECK-NEXT: size 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 + +// CHECK-LABEL: asdf_counted_n +// CHECK: buf 'int * __counted_by_or_null(len)':'int *' +// CHECK-NEXT: len 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 + +// CHECK-LABEL: asdf_sized_n +// CHECK: buf 'int * __sized_by_or_null(size)':'int *' +// CHECK-NEXT: size 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 + +// CHECK-LABEL: asdf_ended +// CHECK: buf 'int * __ended_by(end)':'int *' +// CHECK: end 'int * /* __started_by(buf) */ ':'int *' + +// CHECK-LABEL: asdf_sized_mul +// CHECK: buf 'int * __sized_by(size * count)':'int *' +// CHECK-NEXT: size 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 +// CHECK-NEXT: count 'int' +// CHECK-NEXT: DependerDeclsAttr {{.*}} 0 + +// CHECK-LABEL: asdf_counted_out +// CHECK: buf 'int * __counted_by(*len)*' +// CHECK-NEXT: len 'int *' +// CHECK-NEXT: DependerDeclsAttr {{.*}} IsDeref {{.*}} 1 + +// CHECK-LABEL: asdf_counted_const +// CHECK: buf 'int * __counted_by(7)':'int *' + +// CHECK-LABEL: asdf_counted_nullable +// CHECK: buf 'int * __counted_by(len) _Nullable':'int *' + +// CHECK-LABEL: asdf_counted_noescape +// CHECK: buf 'int * __counted_by(len)':'int *' +// CHECK-NEXT: NoEscapeAttr + +// CHECK-LABEL: asdf_nterm +// CHECK: buf 'int * __terminated_by(0)':'int *' + diff --git a/clang/test/APINotes/templates.cpp b/clang/test/APINotes/templates.cpp index 0556eba925a51..48109011e73a9 100644 --- a/clang/test/APINotes/templates.cpp +++ b/clang/test/APINotes/templates.cpp @@ -7,3 +7,6 @@ // CHECK-BOX: Dumping Box: // CHECK-BOX-NEXT: ClassTemplateDecl {{.+}} imported in Templates Box // CHECK-BOX: SwiftAttrAttr {{.+}} <> "import_owned" + +// Make sure the attributes aren't duplicated. +// CHECK-BOX-NOT: SwiftAttrAttr {{.+}} <> "import_owned" diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp index f5a7481571421..ba32f91a8daf6 100644 --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -230,8 +230,14 @@ __attribute__((external_source_symbol(generated_declaration, defined_in="module" // CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration "testUSR" namespace TestNoEscape { + struct S { int *p; }; void noescapeFunc(int *p0, __attribute__((noescape)) int *p1) {} - // CHECK: `-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)' + // CHECK: |-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)' + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: NoEscapeAttr + void noescapeFunc2(int *p0, __attribute__((noescape)) S p1) {} + // CHECK: `-FunctionDecl{{.*}} noescapeFunc2 'void (int *, __attribute__((noescape)) S)' // CHECK-NEXT: ParmVarDecl // CHECK-NEXT: ParmVarDecl // CHECK-NEXT: NoEscapeAttr diff --git a/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h b/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h new file mode 100644 index 0000000000000..68acd6e429396 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h @@ -0,0 +1,7 @@ +#pragma clang system_header + +inline void FUnspecified(int *x) { + int *y = x; + y = y + 1; + int **z = &y; +} diff --git a/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h b/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h new file mode 100644 index 0000000000000..2633dc2bf06f4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h @@ -0,0 +1,4 @@ +#include + +int *__sized_by(len) alloc_sized_by(int len); +int *alloc_attributed(int len) __attribute__((alloc_size(1))); diff --git a/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h new file mode 100644 index 0000000000000..83450c6d4f38c --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h @@ -0,0 +1,2 @@ +void *myalloc(unsigned); +void *memcpy(void *, void *, unsigned long long); \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h new file mode 100644 index 0000000000000..00f4859f1d735 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h @@ -0,0 +1 @@ +void *myalloc(unsigned) __attribute__((alloc_size(1))); \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c new file mode 100644 index 0000000000000..48bbcea2c24e7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c @@ -0,0 +1,8 @@ +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %S/include/array-param-sys.h --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --implicit-check-not RecoveryExpr + +void foo(int * __counted_by(size) arr, int size) { + funcInSDK(size, arr); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c new file mode 100644 index 0000000000000..1c6eb0b48a343 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c @@ -0,0 +1,312 @@ +#include +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" + +char * __counted_by(len) func(char * __counted_by(len) src_str, int len) { + int len2 = 0; + char * __counted_by(len2) dst_str; + dst_str = __unsafe_forge_bidi_indexable(char*, malloc(len), len); + len2 = len; + memcpy(dst_str, src_str, len); + return dst_str; +} + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | `-OpaqueValueExpr [[ove]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*)(void *__single __sized_by(), const void *__single __sized_by(), unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by()':'const void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_dst_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*)(void *__single __sized_by(), const void *__single __sized_by(), unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by()':'const void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_dst_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*)(void *__single __sized_by(), const void *__single __sized_by(), unsigned long)' +// CHECK: | | | | `-DeclRefExpr {{.+}} +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by()':'const void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'void *' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'void *' +// CHECK: | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_tmp]] +// CHECK: |-FunctionDecl [[func_static_3:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(unsigned long)' +// CHECK: | | `-DeclRefExpr {{.+}} +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: |-FunctionDecl [[func_static_4:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp_1:0x[^ ]+]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *(*)(unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_tmp_1]] +// CHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// CHECK: |-ParmVarDecl [[var_src_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_dst_str:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) <= __builtin_get_pointer_upper_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) && __builtin_get_pointer_lower_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) <= ((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) && len <= __builtin_get_pointer_upper_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) - ((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) && 0 <= len' +// CHECK: | | |-BinaryOperator {{.+}} 'char *__single __counted_by(len2)':'char *__single' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ParenExpr +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ForgePtrExpr +// CHECK: | | |-ParenExpr +// CHECK: | | | `-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: |-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *restrict, void *restrict, int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | `-OpaqueValueExpr [[ove_19]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h new file mode 100644 index 0000000000000..f4e3bc90f788b --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h @@ -0,0 +1,244 @@ +#include + +void extFunc(int size, int arr[size]); +void extFunc3(int size, int * __null_terminated arr); // strict-note{{passing argument to parameter 'arr' here}} + +#pragma clang system_header + +extern void extFunc2(int size, int *sizeLessArr); + +// CHECK: |-FunctionDecl [[func_extFunc:0x[^ ]+]] {{.+}} extFunc +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc3:0x[^ ]+]] {{.+}} extFunc3 +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc2:0x[^ ]+]] {{.+}} 'void (int, int *)' +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_sizeLessArr:0x[^ ]+]] + +static inline void funcInSDK(int size, int arr[size]) { + extFunc(size, arr); + extFunc2(size, arr); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_extFunc]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_extFunc2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +static inline void funcInSDK2(int size, int *sizeLessArr) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(size)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK(size, sizeLessArr); +} + +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_sizeLessArr_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sizeLessArr_1]] +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' + +static inline void funcInSDK3(int size, int arr[size]) { + funcInSDK(size+1, arr); + // strict-error@+2{{passing 'int *__single __counted_by(size)' (aka 'int *__single') to parameter of incompatible type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // strict-note@21{{passing argument to parameter 'arr' here}} + extFunc3(size+1, arr); + // strict-error@+1{{assignment to 'size' requires corresponding assignment to 'int *__single __counted_by(size)' (aka 'int *__single') 'arr'; add self assignment 'arr = arr' if the value has not changed}} + size++; +} + +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size + 1 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size + 1' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __terminated_by(0))' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_extFunc3]] +// CHECK: | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue + +static void tmp() { +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h new file mode 100644 index 0000000000000..c4e61a3fa57a6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h @@ -0,0 +1,24 @@ +#pragma clang system_header + +static inline void memcpy(void *__restrict__ dst, void *__restrict__ src, int size) { + __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy2(void *__restrict__ dst, void *__restrict__ src, int size) { + return __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy3(void *__restrict__ dst, void *__restrict__ src, int size) { + void * tmp = __builtin_memcpy(dst, src, size); + return tmp; +} + +static inline void* malloc(int size) { + return __builtin_malloc(size); +} + +static inline void* malloc2(int size) { + void *tmp = __builtin_malloc(size); + return tmp; +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h new file mode 100644 index 0000000000000..797da3ba99c7a --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h @@ -0,0 +1,5 @@ +#pragma once + +inline void increment_unsafe_p(int *p) { + p++; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h new file mode 100644 index 0000000000000..0219965228e71 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h @@ -0,0 +1,19 @@ +#include +#include + +// both-note@+2{{passing argument to parameter 'p' here}} +// strict-note@+1{{passing argument to parameter 'p' here}} +static inline int * __single funcAdopted(int * __single p) { + return p; +} + +#pragma clang system_header + +static inline int* funcSDK(intptr_t x) { + if (x % 128) + // both-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return funcAdopted(x); + else + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcAdopted((int*)x); +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h new file mode 100644 index 0000000000000..12dcd459e6239 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h @@ -0,0 +1,6 @@ +#ifdef EXTINCLUDE2 +#include +#else +#include "struct-fields-ext.h" +#endif + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h new file mode 100644 index 0000000000000..18fdd09dcdeca --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h @@ -0,0 +1,101 @@ +#pragma clang system_header + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct bar { + int *__ended_by(end) p; + int *end; +}; + +static inline struct foo funcInSDK1(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct foo f = { p, count }; + return f; +} + +static inline struct foo funcInSDK2(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct foo){ p, count }; +} + +static inline struct foo funcInSDK3(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; // This results in a RecoveryExpr, so later analysis cannot see the assignment. + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + + +static inline struct foo funcInSDK4(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; + return f; +} + +static inline struct foo funcInSDK5(int *p, int count) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + +static inline struct bar funcInSDK6(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct bar b = { p, end }; + return b; +} + +static inline struct bar funcInSDK7(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct bar){ p, end }; +} + +static inline struct bar funcInSDK8(int *p, int *end) { + struct bar b; + //strict-error@+1{{assigning to 'int *__single __ended_by(end)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.p = p; + //strict-error@+1{{assigning to 'int *__single /* __started_by(p) */ ' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.end = end; + return b; +} + +static inline struct bar funcInSDK9(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.p' requires corresponding assignment to 'b.end'; add self assignment 'b.end = b.end' if the value has not changed}} + b.p = in.p; + return b; +} + +static inline struct bar funcInSDK10(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.end' requires corresponding assignment to 'b.p'; add self assignment 'b.p = b.p' if the value has not changed}} + b.end = in.end; + return b; +} + +static inline struct foo funcInSDK11(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p' requires corresponding assignment to 'f.count'; add self assignment 'f.count = f.count' if the value has not changed}} + f.p = in.p; + return f; +} + +static inline struct foo funcInSDK12(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = in.count; + return f; +} + +static tmp() { +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h new file mode 100644 index 0000000000000..070f52e923a24 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h @@ -0,0 +1,13 @@ +#include + +//strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(char *__sized_by(4) foo, char *__sized_by(5) bar); + + +#pragma clang system_header + +void funcInSDK(char *ptr, char * __bidi_indexable bidi) { + //strict-error@+1{{passing 'char *' to parameter of incompatible type 'char *__single __sized_by(4)' (aka 'char *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(ptr, bidi); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h new file mode 100644 index 0000000000000..f5c5e9b5e6edc --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h @@ -0,0 +1,44 @@ +#include + +typedef const char * __null_terminated my_str_explicit_t; +typedef const char * my_str_implicit_t; +typedef int * __null_terminated my_nt_int_ptr_t; +typedef int * my_int_ptr_t; + +// both-error@+1{{'__counted_by__' inside typedef is only allowed for function type}} +typedef int * __counted_by(4) ivec4_t; // If this is ever allowed we need to handle it for system headers. + +#pragma clang system_header + +static inline my_str_implicit_t funcInSDK1(const char *p) { + my_str_implicit_t str = p; + return str; + return p; +} + +static inline my_str_explicit_t funcInSDK2(const char *p) { + //strict-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_str_explicit_t str = p; + return str; + //strict-error@+1{{returning 'const char *' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +static inline my_nt_int_ptr_t funcInSDK3(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_nt_int_ptr_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +typedef my_int_ptr_t __null_terminated nt_local_t; + +static inline nt_local_t funcInSDK4(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nt_local_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h new file mode 100644 index 0000000000000..561dc1051d12d --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h @@ -0,0 +1,5 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h new file mode 100644 index 0000000000000..3d44db2f51a52 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h @@ -0,0 +1,216 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; + +// RELAXED: |-VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + sizedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + valueTerminatedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + // This should result in an unsafe BoundsSafetyPointerCast rdar://99202425 +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +void funcInSDK2(int * __single __terminated_by(2) safePointer) { + sizedGlobal = safePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *__single __terminated_by(2)' (aka 'int *__single') requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + valueTerminatedGlobal = safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +void funcInSDK3(int * unsafePointer) { + unsafePointer = sizedGlobal; + unsafePointer = valueTerminatedGlobal; + unsafePointer = bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_2]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_3]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-IntegerLiteral {{.+}} 2 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +void funcInSDK4(int * __single __terminated_by(2) safePointer) { + safePointer = sizedGlobal; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *__single __sized_by(2)' (aka 'int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + safePointer = valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_4]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_5]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: `-CallExpr +// MAINCHECK: |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: `-DeclRefExpr {{.+}} [[var_term]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h new file mode 100644 index 0000000000000..e0e0269f51bde --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h @@ -0,0 +1,9 @@ +#include + +#pragma clang system_header + +// strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(int *__sized_by(4) foo); +void funcWithoutAnnotation(int * foo); +extern int * __single safeGlobal; +extern int * unsafeGlobal; diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h new file mode 100644 index 0000000000000..ff58385741e39 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h @@ -0,0 +1,308 @@ +#include + +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_safeGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// STRICT: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// STRICT: VarDecl [[var_safeGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safeGlobal = unsafePointer; + unsafeGlobal = unsafePointer; + unsafePointer = safeGlobal; + unsafePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK2(int * unsafePointer) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __sized_by(4)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(unsafePointer); + funcWithoutAnnotation(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(4)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | | | `-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// strict-note@+1{{passing argument to parameter 'safePointer' here}} +void funcInSDK3(int * __single safePointer) { + safeGlobal = safePointer; + unsafeGlobal = safePointer; + safePointer = safeGlobal; + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK4(int * __single safePointer) { + funcWithAnnotation(safePointer); + funcWithoutAnnotation(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single' +// RELAXED: | | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsCheckExpr {{.+}} 'safePointer <= __builtin_get_pointer_upper_bound(safePointer) && __builtin_get_pointer_lower_bound(safePointer) <= safePointer && 4 <= (char *)__builtin_get_pointer_upper_bound(safePointer) - (char *)safePointer && 0 <= 4' +// STRICT: | | | | |-CallExpr +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// STRICT: | | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} lower +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'safePointer' 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | | | `-IntegerLiteral {{.+}} 0 +// STRICT: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | `-IntegerLiteral {{.+}} 4 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +void funcInSDK5(int * unsafePointer) { + funcInSDK(unsafePointer); + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK3(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +void funcInSDK6(int * __single safePointer) { + funcInSDK(safePointer); + funcInSDK3(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h new file mode 100644 index 0000000000000..484efba4f7fc9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h @@ -0,0 +1,177 @@ +#include + +// RELAXED: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +int * __unsafe_indexable funcInSDK(int * __unsafe_indexable unsafePointer) { + return unsafePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +int * __unsafe_indexable funcInSDK2(int * __single __terminated_by(2) safePointer) { + return safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +int * __single funcInSDK3(int * __unsafe_indexable unsafePointer) { + return unsafePointer; // strict-error{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +int * __unsafe_indexable funcInSDK4(void) { + return sizedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-MaterializeSequenceExpr {{.+}} +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | `-IntegerLiteral {{.+}} 2 +// STRICT: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +int * __unsafe_indexable funcInSDK5(void) { + return valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +int * __unsafe_indexable funcInSDK6(void) { + return bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK5]] +// MAINCHECK: `-CallExpr +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: `-DeclRefExpr {{.+}} [[func_funcInSDK6]] diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h new file mode 100644 index 0000000000000..4e46443003dca --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h @@ -0,0 +1,17 @@ +#include +#include + +#pragma clang system_header + +typedef void * (*variable_length_function)(va_list args); +static inline void* call_func_internal(variable_length_function f, va_list args) { + return f(args); +} + +static inline void* call_func(variable_length_function f, ...) { + va_list ap; + + va_start(ap, f); + return call_func_internal(f, ap); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c new file mode 100644 index 0000000000000..19cbe54da3c8d --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c @@ -0,0 +1,91 @@ +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=both -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT + +int * func(intptr_t y) { + // both-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcSDK(y); +} + +// RELAXED: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// RELAXED: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_p]] +// +// STRICT: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// STRICT: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_p]] + + +// RELAXED: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// RELAXED: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-IfStmt +// RELAXED: | |-BinaryOperator {{.+}} 'intptr_t':'long' '%' +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_x]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | | `-IntegerLiteral {{.+}} 128 +// RELAXED: | |-ReturnStmt +// RELAXED: | | `-RecoveryExpr +// RELAXED: | | |-DeclRefExpr {{.+}} [[func_static]] +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_x]] +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_static]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-CStyleCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_x]] +// +// STRICT: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// STRICT: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-IfStmt +// STRICT: | |-BinaryOperator {{.+}} 'intptr_t':'long' '%' +// STRICT: | | |-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_x]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | | `-IntegerLiteral {{.+}} 128 +// STRICT: | |-ReturnStmt +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_static]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_x]] +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_static]] +// STRICT: | `-CStyleCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | `-DeclRefExpr {{.+}} [[var_x]] + +// RELAXED: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// RELAXED: |-ParmVarDecl [[var_y:0x[^ ]+]] +// RELAXED: `-CompoundStmt +// RELAXED: `-ReturnStmt +// RELAXED: `-RecoveryExpr +// RELAXED: `-CallExpr +// RELAXED: |-ImplicitCastExpr {{.+}} 'int *(*__single)(intptr_t)' +// RELAXED: | `-DeclRefExpr {{.+}} [[func_static_1]] +// RELAXED: `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: `-DeclRefExpr {{.+}} [[var_y]] +// +// STRICT: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// STRICT: |-ParmVarDecl [[var_y:0x[^ ]+]] +// STRICT: `-CompoundStmt +// STRICT: `-ReturnStmt +// STRICT: `-RecoveryExpr +// STRICT: `-CallExpr +// STRICT: |-ImplicitCastExpr {{.+}} 'int *(*__single)(intptr_t)' +// STRICT: | `-DeclRefExpr {{.+}} [[func_static_1]] +// STRICT: `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: `-DeclRefExpr {{.+}} [[var_y]] diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c new file mode 100644 index 0000000000000..67601d557a1df --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include + +int *__single foo(int *__single *__single); + +void bar(void) { + int *__single s; + foo(&s); +} +// CHECK: `-FunctionDecl {{.+}} bar 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used s 'int *__single' +// CHECK: `-CallExpr {{.+}} 'int *__single' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__single*__single)' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single(int *__single*__single)' Function {{.+}} 'foo' 'int *__single(int *__single*__single)' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single*__single' +// CHECK: `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 's' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c new file mode 100644 index 0000000000000..9d4d1d87f1324 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} +// CHECK: `-FunctionDecl {{.+}} bar 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used s 'int *__unsafe_indexable' +// CHECK: `-CallExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable*__unsafe_indexable)' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable(int *__unsafe_indexable*__unsafe_indexable)' Function {{.+}} 'foo' 'int *__unsafe_indexable(int *__unsafe_indexable*__unsafe_indexable)' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 's' 'int *__unsafe_indexable' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c new file mode 100644 index 0000000000000..f1f47ad136e4f --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c @@ -0,0 +1,115 @@ +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// expected-no-diagnostics + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} + +// RELAXED: TranslationUnitDecl +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | |-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: | `-ParmVarDecl [[var_bar:0x[^ ]+]] +// +// STRICT: TranslationUnitDecl +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | |-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: | `-ParmVarDecl [[var_bar:0x[^ ]+]] + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// RELAXED: | |-ParmVarDecl [[var_bidi:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsCheckExpr +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(char *__single __sized_by(4), char *__single __sized_by(5))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(4)':'char *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(5)':'char *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | |-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} upper +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} lower +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | |-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'long' '-' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} upper +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'long' +// RELAXED: | | | | `-IntegerLiteral {{.+}} 0 +// RELAXED: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_ptr]] +// RELAXED: | | |-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_bidi]] +// RELAXED: | | `-OpaqueValueExpr [[ove_2]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'long' +// RELAXED: | | `-IntegerLiteral {{.+}} 5 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'char *' +// RELAXED: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// STRICT: | |-ParmVarDecl [[var_bidi:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | |-DeclRefExpr {{.+}} [[var_ptr]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidi]] + +// RELAXED: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// RELAXED: |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// RELAXED: |-ParmVarDecl [[var_bidi_1:0x[^ ]+]] +// RELAXED: `-CompoundStmt +// RELAXED: `-CallExpr +// RELAXED: |-ImplicitCastExpr {{.+}} 'void (*__single)(char *, char *__bidi_indexable)' +// RELAXED: | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// RELAXED: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: `-DeclRefExpr {{.+}} [[var_bidi_1]] +// +// STRICT: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// STRICT: |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// STRICT: |-ParmVarDecl [[var_bidi_1:0x[^ ]+]] +// STRICT: `-CompoundStmt +// STRICT: `-CallExpr +// STRICT: |-ImplicitCastExpr {{.+}} 'void (*__single)(char *, char *__bidi_indexable)' +// STRICT: | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// STRICT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: `-DeclRefExpr {{.+}} [[var_bidi_1]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c new file mode 100644 index 0000000000000..eb0eb4d32f488 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c @@ -0,0 +1,14 @@ +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-global-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-global-sys.h +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-global-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-global-sys.h + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(term); +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c new file mode 100644 index 0000000000000..af6517c917212 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c @@ -0,0 +1,15 @@ +#include +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -DSYSHEADER -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-inter-sysheader-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -DSYSHEADER -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-inter-sysheader-sys.h +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT %S/include/unsafe-inter-sysheader-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT %S/include/unsafe-inter-sysheader-sys.h + +void func(int * __unsafe_indexable unsafe, int * __single safe) { + funcInSDK(unsafe); + funcInSDK2(unsafe); + funcInSDK3(safe); + funcInSDK4(safe); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c new file mode 100644 index 0000000000000..b639153f36e74 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c @@ -0,0 +1,18 @@ +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-return-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-return-sys.h +// expected-no-diagnostics +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-return-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-return-sys.h + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(); + funcInSDK5(); + funcInSDK6(); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c new file mode 100644 index 0000000000000..7bed61ab273dd --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c @@ -0,0 +1,88 @@ +#include + +// RUN: %clang_cc1 -triple arm64-apple-macosx -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +// RUN: %clang_cc1 -triple arm64-apple-macosx -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +extern variable_length_function func_ptr; +typedef void * (*variable_length_function2)(va_list args); +extern variable_length_function2 func_ptr2; + +void func(char *dst_str, char *src_str, int len) { + call_func(func_ptr, dst_str, src_str, len); + call_func(func_ptr2, dst_str, src_str, len); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_args:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'va_list':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_args]] +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_ap:0x[^ ]+]] +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*)(__builtin_va_list &, ...)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-DeclRefExpr {{.+}} [[var_ap]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'va_list':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_ap]] +// CHECK: |-VarDecl [[var_func_ptr:0x[^ ]+]] +// CHECK: |-TypedefDecl +// CHECK: | `-PointerType +// CHECK: | `-ParenType +// CHECK: | `-FunctionProtoType +// CHECK: | |-PointerType +// CHECK: | | `-BuiltinType +// CHECK: | `-ElaboratedType +// CHECK: | `-TypedefType +// CHECK: | |-Typedef +// CHECK: | `-ElaboratedType +// CHECK: | `-TypedefType +// CHECK: | |-Typedef +// CHECK: | `-PointerType +// CHECK: | `-BuiltinType +// CHECK: |-VarDecl [[var_func_ptr2:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// CHECK: |-ParmVarDecl [[var_dst_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_src_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), ...)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static_1]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *(*__single)(char *)' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single(*__single)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_func_ptr]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-CallExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), ...)' +// CHECK: | `-DeclRefExpr {{.+}} [[func_static_1]] +// CHECK: |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *(*__single)(char *)' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single(*__single)(va_list)' +// CHECK: | `-DeclRefExpr {{.+}} [[var_func_ptr2]] +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_len]] diff --git a/clang/test/BoundsSafety/AST/__bidi_indexable.c b/clang/test/BoundsSafety/AST/__bidi_indexable.c new file mode 100644 index 0000000000000..130841d0c0c65 --- /dev/null +++ b/clang/test/BoundsSafety/AST/__bidi_indexable.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__bidi_indexable' +}; diff --git a/clang/test/BoundsSafety/AST/__indexable.c b/clang/test/BoundsSafety/AST/__indexable.c new file mode 100644 index 0000000000000..5d18df02bffec --- /dev/null +++ b/clang/test/BoundsSafety/AST/__indexable.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__indexable' +}; diff --git a/clang/test/BoundsSafety/AST/__single.c b/clang/test/BoundsSafety/AST/__single.c new file mode 100644 index 0000000000000..43b781a08462b --- /dev/null +++ b/clang/test/BoundsSafety/AST/__single.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__single foo; + // CHECK: FieldDecl {{.+}} foo 'int *__single' +}; diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c b/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c new file mode 100644 index 0000000000000..eab46e1ffc135 --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void Test() { + int *__bidi_indexable ptr = __unsafe_forge_bidi_indexable(int *, 0, sizeof(int)); + // CHECK: VarDecl {{.+}} ptr 'int *__bidi_indexable' cinit + // CHECK: CStyleCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: ForgePtrExpr {{.+}} 'void *__bidi_indexable' + // CHECK-NEXT: ParenExpr + // CHECK-NEXT: IntegerLiteral {{.+}} 'int' 0 +} diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_single.c b/clang/test/BoundsSafety/AST/__unsafe_forge_single.c new file mode 100644 index 0000000000000..55034feabfb2f --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_single.c @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl {{.+}} Test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} ptr 'int *__single' +// CHECK: `-ParenExpr {{.+}} 'int *__single' +// CHECK: `-CStyleCastExpr {{.+}} 'int *__single'{{.*}} +// CHECK: `-ForgePtrExpr {{.+}} 'void *__single' +// CHECK: |-ParenExpr {{.+}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 17 +// CHECK: |-<<>> +// CHECK: `-<<>> +void Test(void) { + int *__single ptr = __unsafe_forge_single(int *, 17); +} diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c b/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c new file mode 100644 index 0000000000000..5151fd77aef5d --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +void Test() { + int *__terminated_by(42) ptr = __unsafe_forge_terminated_by(int *, 17, 42); +} + +// CHECK: FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'int *{{(__single)?}} __terminated_by(42)':'int *{{(__single)?}}' +// CHECK: `-ForgePtrExpr {{.+}} 'void *{{(__single)?}} __terminated_by((42))':'void *{{(__single)?}}' +// CHECK: |-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 17 +// CHECK: `-ParenExpr +// CHECK: `-IntegerLiteral {{.+}} 42 + +void Test2() { + int **__terminated_by(0) ptr = __unsafe_forge_terminated_by(int **, 17, 0); +} + +// CHECK: FunctionDecl [[func_Test2:0x[^ ]+]] {{.+}} Test2 +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'int *{{(__single)?}}*{{(__single)?}} __terminated_by(0)':'int *{{(__single)?}}*{{(__single)?}}' +// CHECK: `-ForgePtrExpr {{.+}} 'void *{{(__single)?}} __terminated_by((0))':'void *{{(__single)?}}' +// CHECK: |-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 17 +// CHECK: `-ParenExpr +// CHECK: `-IntegerLiteral {{.+}} 0 diff --git a/clang/test/BoundsSafety/AST/__unsafe_indexable.c b/clang/test/BoundsSafety/AST/__unsafe_indexable.c new file mode 100644 index 0000000000000..009cc4eee09e1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_indexable.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__unsafe_indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__unsafe_indexable' +}; diff --git a/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c b/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c new file mode 100644 index 0000000000000..80304ee310682 --- /dev/null +++ b/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c @@ -0,0 +1,126 @@ + +// RUN: %clang_cc1 -verify -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety %s +// RUN: %clang_cc1 -ast-dump -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -verify -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s +// RUN: %clang_cc1 -ast-dump -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +// expected-no-diagnostics + +#include + + +// CHECK-LABEL: FUnspecified 'void (int *)' inline +// CHECK: |-ParmVarDecl {{.*}} used x 'int *' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used y 'int *' cinit +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *' +// CHECK: | `-DeclRefExpr {{.*}} 'int *' lvalue ParmVar {{.*}} 'x' 'int *' +// CHECK: |-BinaryOperator {{.*}} 'int *' '=' +// CHECK: | |-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' +// CHECK: | `-BinaryOperator {{.*}} 'int *' '+' +// CHECK: | |-ImplicitCastExpr {{.*}} 'int *' +// CHECK: | | `-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} z 'int **' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int **' +// CHECK: `-UnaryOperator {{.*}} 'int **__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' + +// CHECK-LABEL: FUnspecifiedInline 'void (int *__single)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__single' +// CHECK: `-CompoundStmt +// CHECK: `-CallExpr +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*__single)(int *)' +// CHECK: | `-DeclRefExpr {{.*}} 'void (int *)' Function {{.*}} 'FUnspecified' 'void (int *)' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK: `-DeclRefExpr {{.*}} 'int *__single' lvalue ParmVar {{.*}} 'x' 'int *__single' +void FUnspecifiedInline(int *x) { + FUnspecified(x); +} + +__ptrcheck_abi_assume_unsafe_indexable() + +// CHECK-LABEL: FUnsafeIndexable 'void (int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used y 'int *__unsafe_indexable' cinit +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue ParmVar {{.*}} 'x' 'int *__unsafe_indexable' +// CHECK: |-CompoundAssignOperator {{.*}} 'int *__unsafe_indexable' '-=' ComputeLHSTy='int *__unsafe_indexable' ComputeResultTy='int *__unsafe_indexable' +// CHECK: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y' 'int *__unsafe_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.*}} 'int' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue ParmVar {{.*}} 'x' 'int *__unsafe_indexable' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} z 'int *__unsafe_indexable*__unsafe_indexable' cinit +// CHECK: `-UnaryOperator {{.*}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y' 'int *__unsafe_indexable' +void FUnsafeIndexable(int *x) { + int *y = x; + y -= *x; + int **z = &y; +} + +// CHECK-LABEL: FUnsafeIndexableAddrOf 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used x 'int' cinit +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} y 'int *__bidi_indexable' cinit +// CHECK: `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'x' 'int' +void FUnsafeIndexableAddrOf(void) { + int x = 0; + int *__bidi_indexable y = &x; +} + +// CHECK-LABEL: FUnsafeIndexableArrayDecay 'void (int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used arr 'int[2]' cinit +// CHECK: | `-InitListExpr {{.*}} 'int[2]' +// CHECK: | |-array_filler: ImplicitValueInitExpr {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} y 'int *__bidi_indexable' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' + +void FUnsafeIndexableArrayDecay(int *x) { + int arr[2] = { 0 }; + int *__bidi_indexable y = arr; +} + +// CHECK-LABEL: FUnsafeIndexableCountedBy 'void (int *__single __counted_by(len), unsigned int)' +// CHECK: |-ParmVarDecl [[var_ptr:0x[^ ]+]] {{.+}} ptr +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] {{.+}} len +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_y_4:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +void FUnsafeIndexableCountedBy(int *__counted_by(len) ptr, unsigned len) { + int *y = ptr; +} diff --git a/clang/test/BoundsSafety/AST/addr-of-ptr.c b/clang/test/BoundsSafety/AST/addr-of-ptr.c new file mode 100644 index 0000000000000..21ad2ab1ffcb0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/addr-of-ptr.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void foo(int *a) { +// CHECK: -VarDecl {{.+}} b 'int *__single*__bidi_indexable' cinit +// CHECK: -VarDecl {{.+}} c 'int *__single*__bidi_indexable' cinit + int *__single *b = &a; + __auto_type c = &a; + + // PointerTypeLoc should have the SourceLocation of __single, __indexable, + // __bidi_indexable, __unsafe_indexable: + // without it, TreeTransform::TransformPointerType doesn't know what + // attributes to use. Keeping this as CHECK-NOT as a reminder to update + // this test when it's fixed. + +// CHECK-NOT: -VarDecl {{.+}} d 'int *__single*__bidi_indexable' cinit +// CHECK-NOT: -VarDecl {{.+}} e 'int *__single*__bidi_indexable' cinit + __auto_type *d = &a; + __auto_type *__single *e = &a; +} diff --git a/clang/test/BoundsSafety/AST/address-of-sizeless.c b/clang/test/BoundsSafety/AST/address-of-sizeless.c new file mode 100644 index 0000000000000..7d3b196064924 --- /dev/null +++ b/clang/test/BoundsSafety/AST/address-of-sizeless.c @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +extern struct incomplete incomplete; +// CHECK: {{^}}|-VarDecl [[var_incomplete:0x[^ ]+]] + +extern struct flexible flexible; +// CHECK: {{^}}|-VarDecl [[var_flexible:0x[^ ]+]] + +extern void void_global; +// CHECK: {{^}}|-VarDecl [[var_void_global:0x[^ ]+]] + +extern void function(void); +// CHECK: {{^}}|-FunctionDecl [[func_function:0x[^ ]+]] {{.+}} 'void (void)' + +extern int array[]; +// CHECK: {{^}}|-VarDecl [[var_array:0x[^ ]+]] + +void address_of(void) { +// CHECK: {{^}}`-FunctionDecl [[func_address_of:0x[^ ]+]] {{.+}} address_of + + (void) &incomplete; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'struct incomplete *__single'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_incomplete]] + + (void) &flexible; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_flexible]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + + (void) &void_global; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'void *__single'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_void_global]] + + (void) &function; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'void (*__single)(void)'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[func_function]] + + (void) &array; +// CHECK: {{^}} `-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} `-UnaryOperator {{.+}} 'int (*__single)[]'{{.*}} prefix '&' +// CHECK: {{^}} `-DeclRefExpr {{.+}} [[var_array]] +} diff --git a/clang/test/BoundsSafety/AST/addrof-deref-attributes.c b/clang/test/BoundsSafety/AST/addrof-deref-attributes.c new file mode 100644 index 0000000000000..bcdbb5a91d4a6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/addrof-deref-attributes.c @@ -0,0 +1,69 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void test(int *__bidi_indexable bidi, int *__indexable unidi, + int *__unsafe_indexable unsafe, int *__null_terminated tb) { + (void)&*bidi; + (void)&bidi[4]; + + (void)&*unidi; + (void)&unidi[4]; + + (void)&*unsafe; + (void)&unsafe[4]; + + (void)&*tb; + (void)&tb[0]; +} + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'bidi' 'int *__bidi_indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'bidi' 'int *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'unidi' 'int *__indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'unidi' 'int *__indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 4 + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'unsafe' 'int *__unsafe_indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'unsafe' 'int *__unsafe_indexable' +// CHECK-NEXT: `-IntegerLiteral + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__single __terminated_by(0)':'int *__single' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'tb' 'int *__single __terminated_by(0)':'int *__single' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__single __terminated_by(0)':'int *__single' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'tb' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral diff --git a/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c b/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c new file mode 100644 index 0000000000000..bab4dd6852df3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c @@ -0,0 +1,109 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include "mock-header.h" + +int foo() { + int cnt = 10; + int siz = sizeof(int); + int *ptr1 = my_calloc(cnt, siz); + int *__bidi_indexable ptr2; + ptr2 = my_calloc(cnt, siz); + return ptr2[cnt-1]; +} + +// CHECK: {{^}}TranslationUnitDecl +// CHECK: {{^}}|-FunctionDecl [[func_my_calloc:0x[^ ]+]] {{.+}} my_calloc +// CHECK: {{^}}| |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}`-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_cnt:0x[^ ]+]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr1:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '*' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr2:0x[^ ]+]] +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}} | |-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '*' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-ArraySubscriptExpr +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int' '-' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 1 diff --git a/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h b/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h new file mode 100644 index 0000000000000..a4e57d475c428 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h @@ -0,0 +1,7 @@ +#ifndef MOCK_HEADER_H +#define MOCK_HEADER_H + +#pragma clang system_header +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); + +#endif /* MOCK_HEADER_H */ diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c new file mode 100644 index 0000000000000..f84d97109c6d5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c @@ -0,0 +1,188 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + len = 12; + buf = arr; +} +// CHECK: -FunctionDecl [[func_param_with_count:0x[^ ]+]] {{.+}} param_with_count +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'int' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 12 - 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= 12 - 2' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-IntegerLiteral {{.+}} 2 +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-IntegerLiteral {{.+}} 12 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' + +void local_count(void) { + int arr[10]; + int len = 8; + int *__counted_by(len + 2) buf = arr; +} +// CHECK: -FunctionDecl [[func_local_count:0x[^ ]+]] {{.+}} local_count +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | |-IntegerLiteral {{.+}} 8 +// CHECK: | `-DependerDeclsAttr +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'int *__single __counted_by(len + 2)':'int *__single' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && len + 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= len + 2' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 2)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: `-OpaqueValueExpr [[ove_4]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-BinaryOperator {{.+}} 'int' '+' +// CHECK: |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: `-IntegerLiteral {{.+}} 2 + +void local_count_size(void) { + size_t nelems; + size_t size; + void *__sized_by(nelems * size) buf; + int arr[10]; + nelems = 10; + size = 4; + buf = arr; +} +// CHECK: -FunctionDecl [[func_local_count_size:0x[^ ]+]] {{.+}} local_count_size +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_nelems:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'size_t':'unsigned long' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 10 * 4 <= (char *)__builtin_get_pointer_upper_bound(arr) - (char *__bidi_indexable)arr' +// CHECK: | | |-BinaryOperator {{.+}} 'size_t':'unsigned long' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_nelems]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-BinaryOperator {{.+}} 'size_t':'unsigned long' '*' +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: |-BinaryOperator {{.+}} 'size_t':'unsigned long' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'size_t':'unsigned long' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'void *__single __sized_by(nelems * size)':'void *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_buf_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(nelems * size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'size_t':'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..709029122af20 --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len); +// CHECK: |-FunctionDecl [[func_param_with_count:0x[^ ]+]] {{.+}} param_with_count +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr + + +void *__sized_by(count * size) return_count_size(size_t count, size_t size); +// CHECK: |-FunctionDecl [[func_return_count_size:0x[^ ]+]] {{.+}} return_count_size +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_size:0x[^ ]+]] + +void calls(void) { + int arr[10]; + param_with_count(arr, 12); + + int *buf = return_count_size(10, 13); +} +// CHECK: `-FunctionDecl [[func_calls:0x[^ ]+]] {{.+}} calls +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'void' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 12 - 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= 12 - 2' +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len - 2), int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_param_with_count]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 12 +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | `-IntegerLiteral {{.+}} 2 +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 13 +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(count * size)(*__single)(size_t, size_t)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_return_count_size]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-BinaryOperator {{.+}} 'size_t':'unsigned long' '*' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'size_t':'unsigned long' + diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c new file mode 100644 index 0000000000000..acf2c5ec34a93 --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +int len = -1; +int *__counted_by(len + 1) buf; +// CHECK: |-VarDecl {{.*}} used len 'int' cinit +// CHECK-NEXT:| |-UnaryOperator {{.*}} 'int' prefix '-' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} buf 'int *__single __counted_by(len + 1)':'int *__single' + +unsigned size; +unsigned count; +void *__sized_by(size * count) buf2; +// CHECK: |-VarDecl {{.*}} used size 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} used count 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} buf2 'void *__single __sized_by(size * count)':'void *__single' + +void f(void) { + int len3 = 10; + int *__counted_by(len3 - 10) buf3; +} +// CHECK-LABEL: f 'void (void)' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used len3 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 10 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:| `-DeclStmt {{.*}} +// CHECK-NEXT:| `-VarDecl {{.*}} buf3 'int *__single __counted_by(len3 - 10)':'int *__single' + +void f2(int *__counted_by(10 * order1 + order0) buf, int order1, unsigned order0); +// CHECK-LABEL: f2 'void (int *__single __counted_by(10 * order1 + order0), int, unsigned int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(10 * order1 + order0)':'int *__single' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} used order1 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used order0 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 + +void *__sized_by(nitems * size) mycalloc(size_t nitems, size_t size); +// CHECK-LABEL: mycalloc 'void *__single __sized_by(nitems * size)(size_t, size_t)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used nitems 'size_t':'unsigned long' +// CHECK-NEXT: `-ParmVarDecl {{.*}} used size 'size_t':'unsigned long' diff --git a/clang/test/BoundsSafety/AST/array-to-pointer-decay.c b/clang/test/BoundsSafety/AST/array-to-pointer-decay.c new file mode 100644 index 0000000000000..89b3ffe03f4f1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/array-to-pointer-decay.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +int glen; +int gArr[5]; + +void Test() { + int *__bidi_indexable ptrGArr = gArr; + // CHECK: VarDecl {{.+}} ptrGArr 'int *__bidi_indexable' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[5]' lvalue Var {{.+}} 'gArr' 'int[5]' + + int arrVLA[glen]; + int *__bidi_indexable ptrVLA = arrVLA; + ptrVLA = arrVLA; + + int arrLocal[7]; + + int *__indexable ptrArrayLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrArrayLocal 'int *__indexable' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrArrayLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'ptrArrayLocal' 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *__single ptrThinLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrThinLocal 'int *__single' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrThinLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__single' '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'ptrThinLocal' 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *ptrUnspecifiedLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrUnspecifiedLocal 'int *__bidi_indexable' + // CHECK: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrUnspecifiedLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__bidi_indexable'{{.*}} '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} 'ptrUnspecifiedLocal' 'int *__bidi_indexable' + // CHECK: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *__bidi_indexable ptrFromArraySub = &arrLocal[0]; +} + diff --git a/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c b/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c new file mode 100644 index 0000000000000..fce0594a4ecf5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c @@ -0,0 +1,124 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that BoundsSafetyPointerCast is emitted when an atomic op is used. + +void unsafe_indexable(void) { + // CHECK: DeclStmt {{.+}} + // CHECK: `-VarDecl {{.+}} p1 '_Atomic(int *__unsafe_indexable)' cinit + // CHECK: `-ImplicitCastExpr {{.+}} '_Atomic(int *__unsafe_indexable)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__bidi_indexable q1; + int *_Atomic __unsafe_indexable p1 = q1; + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p2' '_Atomic(int *__unsafe_indexable)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p2; + int *__bidi_indexable q2; + __c11_atomic_init(&p2, q2); + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p3' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q3' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p3; + int *__bidi_indexable q3; + __c11_atomic_store(&p3, q3, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p4' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q4' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p4; + int *__bidi_indexable q4; + __c11_atomic_exchange(&p4, q4, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p5' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'q5' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r5' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p5; + int *__unsafe_indexable q5; + int *__bidi_indexable r5; + __c11_atomic_compare_exchange_strong(&p5, &q5, r5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void single(void) { + // CHECK: DeclStmt {{.+}} + // CHECK: `-VarDecl {{.+}} p1 '_Atomic(int *__single)' cinit + // CHECK: `-ImplicitCastExpr {{.+}} '_Atomic(int *__single)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__bidi_indexable q1; + int *_Atomic __single p1 = q1; + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p2' '_Atomic(int *__single)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *_Atomic __single p2; + int *__bidi_indexable q2; + __c11_atomic_init(&p2, q2); + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p3' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q3' 'int *__bidi_indexable' + int *_Atomic __single p3; + int *__bidi_indexable q3; + __c11_atomic_store(&p3, q3, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__single' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p4' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q4' 'int *__bidi_indexable' + int *_Atomic __single p4; + int *__bidi_indexable q4; + __c11_atomic_exchange(&p4, q4, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p5' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'q5' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r5' 'int *__bidi_indexable' + int *_Atomic __single p5; + int *__single q5; + int *__bidi_indexable r5; + __c11_atomic_compare_exchange_strong(&p5, &q5, r5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c b/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c new file mode 100644 index 0000000000000..6d3a7a65255bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that BoundsSafetyPointerCast is emitted when an atomic op is used. + +void unsafe_indexable(void) { + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p1' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__unsafe_indexable p1; + int *__bidi_indexable q1; + __atomic_store_n(&p1, q1, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p2' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *__unsafe_indexable p2; + int *__bidi_indexable q2; + __atomic_exchange_n(&p2, q2, __ATOMIC_SEQ_CST); + + // CHECK: `-AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p3' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'q3' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r3' 'int *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} '_Bool' + // CHECK: `-IntegerLiteral {{.+}} 'int' 0 + int *__unsafe_indexable p3; + int *__unsafe_indexable q3; + int *__bidi_indexable r3; + __atomic_compare_exchange_n(&p3, &q3, r3, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void single(void) { + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p1' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__single p1; + int *__bidi_indexable q1; + __atomic_store_n(&p1, q1, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__single' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p2' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *__single p2; + int *__bidi_indexable q2; + __atomic_exchange_n(&p2, q2, __ATOMIC_SEQ_CST); + + // CHECK: `-AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p3' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'q3' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r3' 'int *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} '_Bool' + // CHECK: `-IntegerLiteral {{.+}} 'int' 0 + int *__single p3; + int *__single q3; + int *__bidi_indexable r3; + __atomic_compare_exchange_n(&p3, &q3, r3, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/AST/atomic-types.c b/clang/test/BoundsSafety/AST/atomic-types.c new file mode 100644 index 0000000000000..dc6615222b1f3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-types.c @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void test(void) { + // CHECK: VarDecl {{.+}} p1 '_Atomic(int *__unsafe_indexable)' + int *_Atomic __unsafe_indexable p1; + // CHECK: VarDecl {{.+}} p2 '_Atomic(int *__unsafe_indexable)' + int *__unsafe_indexable _Atomic p2; + // CHECK: VarDecl {{.+}} p3 '_Atomic(int *__unsafe_indexable)' + _Atomic(int *__unsafe_indexable) p3; + // CHECK: VarDecl {{.+}} p4 '_Atomic(int *__unsafe_indexable)' + _Atomic(int *) __unsafe_indexable p4; + + // CHECK: VarDecl {{.+}} p5 '_Atomic(int *__single)' + int *_Atomic __single p5; + // CHECK: VarDecl {{.+}} p6 '_Atomic(int *__single)' + int *__single _Atomic p6; + // CHECK: VarDecl {{.+}} p7 '_Atomic(int *__single)' + _Atomic(int *__single) p7; + // CHECK: VarDecl {{.+}} p8 '_Atomic(int *__single)' + _Atomic(int *) __single p8; + + // CHECK: VarDecl {{.+}} p9 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + int *_Atomic __unsafe_indexable *_Atomic __unsafe_indexable p9; + // CHECK: VarDecl {{.+}} p10 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + int *__unsafe_indexable _Atomic *_Atomic __unsafe_indexable p10; + // CHECK: VarDecl {{.+}} p11 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + _Atomic(int *__unsafe_indexable) *_Atomic __unsafe_indexable p11; + // CHECK: VarDecl {{.+}} p12 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + _Atomic(int *) __unsafe_indexable *_Atomic __unsafe_indexable p12; + + // CHECK: VarDecl {{.+}} p13 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *_Atomic __single *_Atomic __unsafe_indexable p13; + // CHECK: VarDecl {{.+}} p14 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *__single _Atomic *_Atomic __unsafe_indexable p14; + // CHECK: VarDecl {{.+}} p15 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *__single) *_Atomic __unsafe_indexable p15; + // CHECK: VarDecl {{.+}} p16 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *) __single *_Atomic __unsafe_indexable p16; + + // CHECK: VarDecl {{.+}} p17 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + int *_Atomic __unsafe_indexable *_Atomic __single p17; + // CHECK: VarDecl {{.+}} p18 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + int *__unsafe_indexable _Atomic *_Atomic __single p18; + // CHECK: VarDecl {{.+}} p19 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + _Atomic(int *__unsafe_indexable) *_Atomic __single p19; + // CHECK: VarDecl {{.+}} p20 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + _Atomic(int *) __unsafe_indexable *_Atomic __single p20; + + // CHECK: VarDecl {{.+}} p21 '_Atomic(_Atomic(int *__single) *__single)' + int *_Atomic __single *_Atomic __single p21; + // CHECK: VarDecl {{.+}} p22 '_Atomic(_Atomic(int *__single) *__single)' + int *__single _Atomic *_Atomic __single p22; + // CHECK: VarDecl {{.+}} p23 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *__single) *_Atomic __single p23; + // CHECK: VarDecl {{.+}} p24 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *) __single *_Atomic __single p24; +} diff --git a/clang/test/BoundsSafety/AST/attr-applied-twice.c b/clang/test/BoundsSafety/AST/attr-applied-twice.c new file mode 100644 index 0000000000000..34a78af78d0d9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/attr-applied-twice.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s + +// When processing the type for `b`, the types are processed from the beginning of the declaration, +// resulting in the type attribute being applied despite having been applied already + +#include + +typedef int * int_ptr_t; +void foo() { + int_ptr_t __single a, b; + int_ptr_t __bidi_indexable c, d; + int_ptr_t __unsafe_indexable e, f; + int_ptr_t __indexable g, h; +} + +// CHECK: -FunctionDecl {{.*}} foo 'void ()' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} a 'int_ptr_t __single':'int *__single' +// CHECK-NEXT: | `-VarDecl {{.*}} b 'int_ptr_t __single':'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} c 'int_ptr_t __bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: | `-VarDecl {{.*}} d 'int_ptr_t __bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} e 'int_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK-NEXT: | `-VarDecl {{.*}} f 'int_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: |-VarDecl {{.*}} g 'int_ptr_t __indexable':'int *__indexable' +// CHECK-NEXT: `-VarDecl {{.*}} h 'int_ptr_t __indexable':'int *__indexable' + diff --git a/clang/test/BoundsSafety/AST/auto-bound-array.c b/clang/test/BoundsSafety/AST/auto-bound-array.c new file mode 100644 index 0000000000000..eec6c582278de --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-array.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +void foo(void) { + // CHECK: VarDecl {{.+}} array_of_ptrs 'int *__single[2]' + int *array_of_ptrs[2]; + + // CHECK: VarDecl {{.+}} ptr_to_array 'int (*__bidi_indexable)[3]' + int(*ptr_to_array)[3]; + + // CHECK: VarDecl {{.+}} array_of_ptrs_to_arrays 'int (*__single[2])[3]' + int(*array_of_ptrs_to_arrays[2])[3]; + + // CHECK: VarDecl {{.+}} ptr_to_ptr_to_array 'int (*__single*__bidi_indexable)[3]' + int(**ptr_to_ptr_to_array)[3]; + + // CHECK: VarDecl {{.+}} ptr_to_array_of_ptrs 'int *__single(*__bidi_indexable)[3]' + int *(*ptr_to_array_of_ptrs)[3]; + + // CHECK: VarDecl {{.+}} ptr_to_array_of_ptrs_to_arrays 'int (*__single(*__bidi_indexable)[4])[3]' + int(*(*ptr_to_array_of_ptrs_to_arrays)[4])[3]; +} diff --git a/clang/test/BoundsSafety/AST/auto-bound-atomic.c b/clang/test/BoundsSafety/AST/auto-bound-atomic.c new file mode 100644 index 0000000000000..d57b41dc336dd --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-atomic.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct field { + // CHECK: FieldDecl {{.+}} f1 '_Atomic(int *__single)' + int *_Atomic f1; + // CHECK: FieldDecl {{.+}} f2 '_Atomic(int *__single)' + _Atomic(int *) f2; +}; + +// CHECK: FunctionDecl {{.+}} ret1 '_Atomic(int *__single) (void)' +int *_Atomic ret1(void); +// CHECK: FunctionDecl {{.+}} ret2 '_Atomic(int *__single) (void)' +_Atomic(int *) ret2(void); + +// CHECK: ParmVarDecl {{.+}} p1 '_Atomic(int *__single)' +void parm1(int *_Atomic p1); +// CHECK: ParmVarDecl {{.+}} p2 '_Atomic(int *__single)' +void parm2(_Atomic(int *) p2); + +void locals(void) { + // CHECK: VarDecl {{.+}} l1 '_Atomic(_Atomic(int *__single) *__single)' + int *_Atomic *_Atomic __single l1; + // CHECK: VarDecl {{.+}} l2 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *) *_Atomic __single l2; + + // CHECK: VarDecl {{.+}} l3 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *_Atomic *_Atomic __unsafe_indexable l3; + // CHECK: VarDecl {{.+}} l4 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *) *_Atomic __unsafe_indexable l4; +} diff --git a/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h new file mode 100644 index 0000000000000..0866fc9d50f74 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +void system_func(const char *p); diff --git a/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c new file mode 100644 index 0000000000000..88cda6f845449 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c @@ -0,0 +1,141 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include +#include + +/* + * The cases where __null_terminated should be added: + */ + +// CHECK: ParmVarDecl {{.+}} const_char_parm 'const char *__single __terminated_by(0)' +void const_char_parm(const char *const_char_parm); + +// CHECK: ParmVarDecl {{.+}} const_wchar_t_param 'const wchar_t *__single __terminated_by(0)' +void const_wchar_t_param(const wchar_t *const_wchar_t_param); + +// CHECK: ParmVarDecl {{.+}} out_const_char_parm 'const char *__single __terminated_by(0)*__single' +void out_const_char_parm(const char **out_const_char_parm); + +// CHECK: ParmVarDecl {{.+}} fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +void fptr_param(const char *(*fptr)(const char *p)); + +// CHECK: ParmVarDecl {{.+}} p1 'const char *__single __terminated_by(0)' +// CHECK: ParmVarDecl {{.+}} p2 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0), const char *__single __terminated_by(0))' +// CHECK: ParmVarDecl {{.+}} p3 'const char *__single __terminated_by(0)' +void multiple_parms(const char *p1, const char *(*p2)(const char *a, const char *b), const char *p3); + +// CHECK: FunctionDecl {{.+}} const_char_ret 'const char *__single __terminated_by(0)(void)' +const char *const_char_ret(void); + +// CHECK: FunctionDecl {{.+}} fptr_ret_proto 'const char *__single __terminated_by(0)(*__single(void))(const char *__single __terminated_by(0))' +const char *(*fptr_ret_proto(void))(const char *); + +// CHECK: FunctionDecl {{.+}} fptr_ret_no_proto 'const char *__single __terminated_by(0)(*__single())(const char *__single __terminated_by(0))' +const char *(*fptr_ret_no_proto())(const char *); + +void foo(void) { + // CHECK: VarDecl {{.+}} local_fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' + const char *(*local_fptr)(const char *p); + + // CHECK: VarDecl {{.+}} ptr_const_char_local 'const char *__single __terminated_by(0)*__bidi_indexable' + const char **ptr_const_char_local; + + // CHECK: VarDecl {{.+}} local_fptr_array 'const char *__single __terminated_by(0)(*__single[42])(const char *__single __terminated_by(0))' + const char *(*local_fptr_array[42])(const char *p); + + // CHECK: VarDecl {{.+}} local_fptr_array_ptr 'const char *__single __terminated_by(0)(*__single(*__bidi_indexable)[42])(const char *__single __terminated_by(0))' + const char *(*(*local_fptr_array_ptr)[42])(const char *p); +} + +// CHECK: VarDecl {{.+}} const_char_global 'const char *__single __terminated_by(0)' +const char *const_char_global; + +// CHECK: VarDecl {{.+}} global_fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +const char *(*global_fptr)(const char *p); + +struct const_char_struct { + // CHECK: FieldDecl {{.+}} const_char_field 'const char *__single __terminated_by(0)' + const char *const_char_field; +}; + +typedef const char *my_func_t(const char *p); +typedef const char *(*my_func_ptr_t)(const char *p); + +// CHECK: FunctionDecl {{.+}} typedef_func 'const char *__single __terminated_by(0)(const char *__single __terminated_by(0))' +my_func_t typedef_func; + +// CHECK: VarDecl {{.+}} typedef_func_ptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +my_func_ptr_t typedef_func_ptr; + +// CHECK: ParmVarDecl {{.+}} quals_c 'const char *__single __terminated_by(0)const':'const char *__singleconst' +void quals_c(const char *const quals_c); + +// CHECK: ParmVarDecl {{.+}} quals_cv 'const char *__single __terminated_by(0)const volatile':'const char *__singleconst volatile' +void quals_cv(const char *const volatile quals_cv); + +/* + * The cases where __null_terminated should NOT be added: + */ + +void bar(void) { + // CHECK: VarDecl {{.+}} const_char_local 'const char *__bidi_indexable' + const char *const_char_local; + + // CHECK: VarDecl {{.+}} local_fptr 'const char *__single(*__single)(const char *__single __counted_by(8))' + const char *__single (*local_fptr)(const char *__counted_by(8) p); +} + +// CHECK: ParmVarDecl {{.+}} const_int_parm 'const int *__single' +void const_int_parm(const int *const_int_parm); + +// CHECK: ParmVarDecl {{.+}} const_int8_t_param 'const int8_t *__single' +void const_int8_t_param(const int8_t *const_int8_t_param); + +// CHECK: ParmVarDecl {{.+}} char_parm 'char *__single' +void char_parm(char *char_parm); + +// CHECK: ParmVarDecl {{.+}} unsafe_parm 'const char *__unsafe_indexable' +void unsafe_parm(const char *__unsafe_indexable unsafe_parm); + +// CHECK: ParmVarDecl {{.+}} single_parm 'const char *__single' +void single_parm(const char *__single single_parm); + +// CHECK: ParmVarDecl {{.+}} bidi_parm 'const char *__bidi_indexable' +void bidi_parm(const char *__bidi_indexable bidi_parm); + +// CHECK: ParmVarDecl {{.+}} counted_parm 'const char *__single __counted_by(8)' +void counted_parm(const char *__counted_by(8) counted_parm); + +// CHECK: ParmVarDecl {{.+}} ended_parm 'const char *__single __ended_by(end)' +// CHECK: ParmVarDecl {{.+}} end 'const char *__single /* __started_by(ended_parm) */ ':'const char *__single' +void ended_parm_single(const char *__ended_by(end) ended_parm, const char *__single end); + +// CHECK: ParmVarDecl {{.+}} ended_parm 'const char *__single __ended_by(end)' +// CHECK: ParmVarDecl {{.+}} end 'const char *__single /* __started_by(ended_parm) */ ':'const char *__single' +void ended_parm_unspec(const char *__ended_by(end) ended_parm, const char *end); + +// CHECK: ParmVarDecl {{.+}} nt_parm 'const char *__single __terminated_by(0)' +void nt_parm(const char *__null_terminated nt_parm); + +// CHECK: ParmVarDecl {{.+}} tb_parm 'const char *__single __terminated_by('X')' +void tb_parm(const char *__terminated_by('X') tb_parm); + +// CHECK: ParmVarDecl {{.+}} out_single 'const char *__single*__single' +void out_single(const char *__single *out_single); + +// CHECK: ParmVarDecl {{.+}} out_counted 'const char *__single __counted_by(8)*__single' +void out_counted(const char *__counted_by(8) * out_counted); + +// CHECK: FunctionDecl {{.+}} system_func 'void (const char *)' +#include "auto-bound-const-char-pointer-param-system.h" + +__ptrcheck_abi_assume_unsafe_indexable(); + +// CHECK: FunctionDecl {{.+}} unsafe_abi 'void (const char *__unsafe_indexable)' +void unsafe_abi(const char *p); + +// Make sure we add __single to __terminated_by() even if the ABI is __unsafe_indexable. +// CHECK: FunctionDecl {{.+}} unsafe_abi_explicit 'void (const char *__single __terminated_by(0))' +void unsafe_abi_explicit(const char *__null_terminated p); diff --git a/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c b/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c new file mode 100644 index 0000000000000..88dbf29b77570 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +// CHECK: VarDecl {{.+}} g_var_proto 'int *__single(*__single)(int *__single)' +int *(*g_var_proto)(int *p); + +// CHECK: VarDecl {{.+}} g_var_proto_va 'int *__single(*__single)(int *__single, ...)' +int *(*g_var_proto_va)(int *p, ...); + +// CHECK: VarDecl {{.+}} g_var_no_proto 'int *__single(*__single)()' +int *(*g_var_no_proto)(); + +void foo(void) { + // CHECK: VarDecl {{.+}} l_var_proto 'int *__single(*__single)(int *__single)' + int *(*l_var_proto)(int *p); + + // CHECK: VarDecl {{.+}} l_var_proto_va 'int *__single(*__single)(int *__single, ...)' + int *(*l_var_proto_va)(int *p, ...); + + // CHECK: VarDecl {{.+}} l_var_no_proto 'int *__single(*__single)()' + int *(*l_var_no_proto)(); +} + +// CHECK: ParmVarDecl {{.+}} param_proto 'int *__single(*__single)(int *__single)' +void f1(int *(*param_proto)(int *p)); + +// CHECK: ParmVarDecl {{.+}} param_proto_va 'int *__single(*__single)(int *__single, ...)' +void f2(int *(*param_proto_va)(int *p, ...)); + +// CHECK: ParmVarDecl {{.+}} param_no_proto 'int *__single(*__single)()' +void f3(int *(*param_no_proto)()); + +// CHECK: FunctionDecl {{.+}} ret_proto 'int *__single(*__single(void))(int *__single)' +int *(*ret_proto(void))(int *p); + +// CHECK: FunctionDecl {{.+}} ret_proto_va 'int *__single(*__single(void))(int *__single, ...)' +int *(*ret_proto_va(void))(int *p, ...); + +// CHECK: FunctionDecl {{.+}} ret_no_proto 'int *__single(*__single(void))()' +int *(*ret_no_proto(void))(); + +struct bar { + // CHECK: FieldDecl {{.+}} field_proto 'int *__single(*__single)(int *__single)' + int *(*field_proto)(int *p); + + // CHECK: FieldDecl {{.+}} field_proto_va 'int *__single(*__single)(int *__single, ...)' + int *(*field_proto_va)(int *p, ...); + + // CHECK: FieldDecl {{.+}} field_no_proto 'int *__single(*__single)()' + int *(*field_no_proto)(); +}; + +// CHECK: FunctionDecl {{.+}} nested 'int *__single(*__single(int *__single(*__single)(int *__single)))(int *__single(*__single)(int *__single))' +int *(*nested(int *(*)(int *)))(int *(*)(int *)); + +// CHECK: VarDecl {{.+}} array 'int *__single(*__single[42])(int *__single)' +int *(*array[42])(int *); + +struct digest { + // CHECK: FieldDecl {{.+}} di 'const struct ccdigest_info *__single(*__single)(void)' + const struct ccdigest_info *(*di)(void); +}; diff --git a/clang/test/BoundsSafety/AST/auto-bound-local.c b/clang/test/BoundsSafety/AST/auto-bound-local.c new file mode 100644 index 0000000000000..6054246712b56 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-local.c @@ -0,0 +1,64 @@ +// FIXME: rdar://69452444 +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s --check-prefix=CHECK-M2 +// RUN: not %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s --check-prefix=CHECK-M2 +#include + + +void Test(void) { + int *localAddrTaken; + int **pp = &localAddrTaken; + int *localNoAddrTaken; + int *__bidi_indexable localBoundAddrTaken; + int *__bidi_indexable *boundPP = &localBoundAddrTaken; + void (*fptr)(void) = &Test; + void (*fptr2)(void) = Test; +} + +// FIXME: rdar://69452444 +// CHECK: `-FunctionDecl {{.+}} Test 'void (void)' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} used localAddrTaken 'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} pp 'int *__single*__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'localAddrTaken' 'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} localNoAddrTaken 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} localBoundAddrTaken 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} boundPP 'int *__bidi_indexable*__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{0x[a-z0-9]*}} 'localBoundAddrTaken' 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} fptr 'void (*__single)(void)':'void (*__single)(void)' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'void (*__single)(void)' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.+}} fptr2 'void (*__single)(void)':'void (*__single)(void)' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(void)' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' + +// CHECK-M2: `-FunctionDecl +// CHECK-M2: `-CompoundStmt +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} used localAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} pp 'int *__single*__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} localNoAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} used localBoundAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} boundPP 'int *__bidi_indexable*__bidi_indexable' +// CHECK-M2: | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK-M2: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'localBoundAddrTaken' 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} fptr 'void (*__single)(void)' cinit +// CHECK-M2: | `-UnaryOperator {{.+}} 'void (*__single)(void)' prefix '&' cannot overflow +// CHECK-M2: | `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' +// CHECK-M2: `-DeclStmt +// CHECK-M2: `-VarDecl {{.+}} fptr2 'void (*__single)(void)' cinit +// CHECK-M2: `-ImplicitCastExpr {{.+}} 'void (*__single)(void)' +// CHECK-M2: `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' diff --git a/clang/test/BoundsSafety/AST/auto-bound-var-storage.c b/clang/test/BoundsSafety/AST/auto-bound-var-storage.c new file mode 100644 index 0000000000000..1b7b5f39ede90 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-var-storage.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: VarDecl {{.+}} global 'int *__single' +int *global; + +// CHECK: VarDecl {{.+}} static_global 'int *__single' static +static int *static_global; + +// CHECK: VarDecl {{.+}} extern_global 'int *__single' extern +extern int *extern_global; + +// CHECK: VarDecl {{.+}} private_extern_global 'int *__single' __private_extern__ +__private_extern__ int *private_extern_global; + +// CHECK: VarDecl {{.+}} thread_global 'int *__single' tls +__thread int *thread_global; + +// CHECK: VarDecl {{.+}} static_thread_global 'int *__single' static tls +static __thread int *static_thread_global; + +// CHECK: VarDecl {{.+}} extern_thread_global 'int *__single' extern tls +extern __thread int *extern_thread_global; + +// CHECK: VarDecl {{.+}} private_extern_thread_global 'int *__single' __private_extern__ tls +__private_extern__ __thread int *private_extern_thread_global; + +void foo(void) { + // CHECK: VarDecl {{.+}} local 'int *__bidi_indexable' + int *local; + + // CHECK: VarDecl {{.+}} static_local 'int *__single' static + static int *static_local; + + // CHECK: VarDecl {{.+}} extern_local 'int *__single' extern + extern int *extern_local; + + // CHECK: VarDecl {{.+}} private_extern_local 'int *__single' __private_extern__ + __private_extern__ int *private_extern_local; + + // CHECK: VarDecl {{.+}} static_thread_local 'int *__single' static tls + static __thread int *static_thread_local; + + // CHECK: VarDecl {{.+}} extern_thread_local 'int *__single' extern tls + extern __thread int *extern_thread_local; + + // CHECK: VarDecl {{.+}} private_extern_thread_local 'int *__single' __private_extern__ tls + __private_extern__ __thread int *private_extern_thread_local; +} diff --git a/clang/test/BoundsSafety/AST/auto-type-from-decayed.c b/clang/test/BoundsSafety/AST/auto-type-from-decayed.c new file mode 100644 index 0000000000000..d1311d2e313c1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-type-from-decayed.c @@ -0,0 +1,62 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +int i; +int arr[2]; +void Foo(int *__counted_by(len) ptr, unsigned len); + +__auto_type glob_from_addrof = &i; /* int *__bidi_indexable */ +__auto_type glob_from_arrdecay = arr; /* int *__bidi_indexable */ +__auto_type glob_from_addr_of_arr = &arr[1]; /* int *__bidi_indexable */ +__auto_type glob_from_fundecay = Foo; /* int *__single */ + +void Test() { + int *__single single_ptr; + int *__indexable index_ptr; + int *__unsafe_indexable unsafe_ptr; + + __auto_type local_from_arrdecay = arr; /* int *__bidi_indexable */ + __auto_type local_from_addrof = &i; /* int *__bidi_indexable */ + __auto_type local_from_addr_of_arr = &arr[0]; /* int *__bidi_indexable */ + __auto_type local_from_fundecay = Foo; /* void(*__single)(int *__counted_by(), unsigned) */ + __auto_type local_from_single = single_ptr; /* int *__single */ + __auto_type local_from_index = index_ptr; /* int *__indexable */ + __auto_type local_from_unsafe = unsafe_ptr; /* int *__unsafe_indexable */ +} + +// CHECK: `-FunctionDecl {{.*}} line:17:6 Test 'void ()' +// CHECK: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_arrdecay 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_addrof 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'i' 'int' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_addr_of_arr 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.*}} 'int' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' +// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_fundecay 'void (*__single)(int *__single __counted_by(len), unsigned int)' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'void (*__single)(int *__single __counted_by(len), unsigned int)' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void (int *__single __counted_by(len), unsigned int)' Function {{.*}} 'Foo' 'void (int *__single __counted_by(len), unsigned int)' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_single 'int *__single' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'single_ptr' 'int *__single' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_index 'int *__indexable' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'index_ptr' 'int *__indexable' +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} col:15 local_from_unsafe 'int *__unsafe_indexable' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'unsafe_ptr' 'int *__unsafe_indexable' diff --git a/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c b/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c new file mode 100644 index 0000000000000..0d32fbd57f4eb --- /dev/null +++ b/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c @@ -0,0 +1,115 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -Wno-bounds-safety-init-list %s | FileCheck %s +#include + +void Test(void) { + int *__single iptr; + int len; + char *__counted_by(len) cptr = iptr; + int len2; + long *__counted_by(len2) lptr = cptr; + return; +} + +// CHECK-LABEL: Test +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_iptr:0x[^ ]+]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_cptr:0x[^ ]+]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_iptr]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_lptr:0x[^ ]+]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'long *__single __counted_by(len2)':'long *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_cptr]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len2]] diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c b/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c new file mode 100644 index 0000000000000..ea73a7a716e2f --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c @@ -0,0 +1,752 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump -verify %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump -verify %s | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK: {{^}}|-FunctionDecl [[func_init_local:0x[^ ]+]] {{.+}} init_local +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count1 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count2 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count2)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count3 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count3)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| `-DeclStmt +// CHECK-NEXT: {{^}}| `-VarDecl [[var_p4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count4 == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_count4]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +void init_local(void) { + int count = 0; + int* __counted_by(count) p = (int*) 0; + + int count1 = 0; + int* __counted_by(count1) p1 = (void*) 0; + + int count2 = 0; + int* __counted_by(count2) p2 = (int*)(void*) 0; + + int count3 = 0; + int* __counted_by(count3) p3 = ((int*)(void*)(int*) 0); + + int count4 = 0; + int* __counted_by(count4) p4 = 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_assign_local:0x[^ ]+]] {{.+}} assign_local +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count1_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count1)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p1_1]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count2_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count2)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count2)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count3_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count3)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p3_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count3)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count4_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(count4)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p4_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| `-FieldDecl +void assign_local(void) { + int count = 0; + int* __counted_by(count) p; + int count1 = 0; + int* __counted_by(count1) p1; + int count2 = 0; + int* __counted_by(count2) p2; + int count3 = 0; + int* __counted_by(count3) p3; + int count4 = 0; + int* __counted_by(count4) p4; + + count = 0; + p = (int*)0; + + count1 = 0; + p1 = (void*) 0; + + count2 = 0; + p2 = (int*)(void*) 0; + + count3 = 0; + p3 = ((int*)(void*)(int*) 0); + + count4 = 0; + p4 = 0; +} + +struct Cb { + int count; + int* __counted_by(count) ptr; +}; + +// CHECK: {{^}}|-FunctionDecl [[func_init_struct:0x[^ ]+]] {{.+}} init_struct +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-DeclStmt +// CHECK-NEXT: {{^}}| `-VarDecl [[var_c4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| |-InitListExpr +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +void init_struct(void) { + struct Cb c = { 0, (int*)0}; + struct Cb c1 = { 0, (void*)0}; + struct Cb c2 = { 0, (int*)(void*)0}; + struct Cb c3 = { 0, ((int*)(void*)(int*)0)}; + struct Cb c4 = { 0, 0}; +} + +// CHECK: {{^}}|-FunctionDecl [[func_assign_struct:0x[^ ]+]] {{.+}} assign_struct +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c1_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c1_1]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c2_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c2_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c3_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c3_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c4_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_c4_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +void assign_struct(void) { + struct Cb c; + struct Cb c1; + struct Cb c2; + struct Cb c3; + struct Cb c4; + + c.count = 0; + c.ptr = (int*)0; + + c1.count = 0; + c1.ptr = (void*)0; + + c2.count = 0; + c2.ptr = (int*)(void*)0; + + c3.count = 0; + c3.ptr = ((int*)(void*)(int*)0); + + c4.count = 0; + c4.ptr = 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_receive_cb:0x[^ ]+]] {{.+}} receive_cb +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| `-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +void receive_cb(int* __counted_by(count), int count); + +// CHECK: {{^}}`-FunctionDecl [[func_call_arg:0x[^ ]+]] {{.+}} call_arg +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(int *)0 <= (int *)0 && (int *)0 <= (int *)0 && count <= (int *)0 - (int *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_31]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(void *)0 <= (void *)0 && (void *)0 <= (void *)0 && count <= (void *)0 - (void *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_33]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(int *)(void *)0 <= (int *)(void *)0 && (int *)(void *)0 <= (int *)(void *)0 && count <= (int *)(void *)0 - (int *)(void *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_34]] +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_35]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '((int *)(void *)(int *)0) <= ((int *)(void *)(int *)0) && ((int *)(void *)(int *)0) <= ((int *)(void *)(int *)0) && count <= ((int *)(void *)(int *)0) - ((int *)(void *)(int *)0) && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_37:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_36]] +// CHECK-NEXT: {{^}} | | | `-ParenExpr +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_37]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && count <= 0 - 0 && 0 <= count' +// CHECK-NEXT: {{^}} | | |-CallExpr +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_38]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +void call_arg(int count) { + receive_cb((int*)0, count); + receive_cb((void*)0, count); + receive_cb((int*)(void*)0, count); + receive_cb(((int*)(void*)(int*)0), count); + receive_cb(0, count); +} + +// Assignment on returns and in compound-literals are handled elsewhere diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c new file mode 100644 index 0000000000000..0e75a5e4534bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c @@ -0,0 +1,126 @@ + + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// TODO: Remove this test when return-size checks are enabled by default. + +// CHECK: FunctionDecl [[func_cb_in_from_bidi:0x[^ ]+]] {{.+}} cb_in_from_bidi +// CHECK: |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p]] +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_indexable:0x[^ ]+]] {{.+}} cb_in_from_indexable +// CHECK: |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_1]] +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_single:0x[^ ]+]] {{.+}} cb_in_from_single +// CHECK: |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_2]] +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_int_cast_null:0x[^ ]+]] {{.+}} cb_in_from_int_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_int_cast_null(int count) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_void_cast_null:0x[^ ]+]] {{.+}} cb_in_from_void_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_void_cast_null(int count) { + return (void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_int_void_int_cast_null:0x[^ ]+]] {{.+}} cb_in_from_int_void_int_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_int_void_int_cast_null(int count) { + return (int*)(void*)(int*)0; +} + +// CHECK: FunctionDecl [[func_cb_out_from_single:0x[^ ]+]] {{.+}} cb_out_from_single +// CHECK: |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_3]] +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_sb_from_single:0x[^ ]+]] {{.+}} sb_from_single +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_4]] +void *__sized_by(size) sb_from_single(int size, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cbn_in_from_single:0x[^ ]+]] {{.+}} cbn_in_from_single +// CHECK: |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_5]] +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_eb_in_from_single:0x[^ ]+]] {{.+}} eb_in_from_single +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_6]] +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c new file mode 100644 index 0000000000000..ad9e434e69176 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c @@ -0,0 +1,20 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure we don't add bounds checks to RecoveryExprs +// CHECK-LABEL:`-FunctionDecl {{.+}} <{{.+}}, line:{{.+}}> line:{{.+}} no_bounds_check_expr_ret 'int *__single __sized_by(2)(int *__single __sized_by(3))' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *__single __sized_by(3)':'int *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-ReturnStmt {{.+}} +// CHECK-NEXT: `-RecoveryExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' contains-errors +// CHECK-NEXT: `-FloatingLiteral {{.+}} 'float' 0.000000e+00 +int* __sized_by(2) no_bounds_check_expr_ret(int* __sized_by(3) p) { + return 0.0f; +} + diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c new file mode 100644 index 0000000000000..92f58c31c954e --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -ast-dump -verify %s > %t.c.ast_dump.txt 2>&1 +// RUN: FileCheck --input-file=%t.c.ast_dump.txt %S/bounds-attributed-in-return-null-system-header.h +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -ast-dump -verify %s > %t.objc.ast_dump.txt 2>&1 +// RUN: FileCheck --input-file=%t.objc.ast_dump.txt %S/bounds-attributed-in-return-null-system-header.h + +#include "bounds-attributed-in-return-null-system-header.h" + +// expected-no-diagnostics + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_explicit_unspecified_cast_0(int count) { + return inline_header_ret_explicit_unspecified_cast_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_explicit_unsafe_indexable_cast_0(int count) { + return inline_header_ret_explicit_unsafe_indexable_cast_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_0(int count) { + return inline_header_ret_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_void_star_unspecified_0(int count) { + return inline_header_ret_void_star_unspecified_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_void_star_unsafe_indexable_0(int count) { + return inline_header_ret_void_star_unsafe_indexable_0(count); +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h new file mode 100644 index 0000000000000..508e0dc25f0f2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h @@ -0,0 +1,530 @@ +// Pretend this is a system header +#pragma clang system_header +#include + +// FIXME: We might not want bounds checks here because this file is in something +// that hasn't fully adopted (rdar://139815437) + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_explicit_unspecified_cast_0:0x[^ ]+]] {{.+}} inline_header_ret_explicit_unspecified_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count]] +inline int* __counted_by(count) inline_header_ret_explicit_unspecified_cast_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_explicit_unsafe_indexable_cast_0:0x[^ ]+]] {{.+}} inline_header_ret_explicit_unsafe_indexable_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_1]] +inline int* __counted_by(count) inline_header_ret_explicit_unsafe_indexable_cast_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (int* __unsafe_indexable)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_0:0x[^ ]+]] {{.+}} inline_header_ret_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_2]] +inline int* __counted_by(count) inline_header_ret_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_void_star_unspecified_0:0x[^ ]+]] {{.+}} inline_header_ret_void_star_unspecified_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_3]] +inline int* __counted_by(count) inline_header_ret_void_star_unspecified_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (void*)0; +} + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_inline_header_ret_void_star_unsafe_indexable_0:0x[^ ]+]] {{.+}} inline_header_ret_void_star_unsafe_indexable_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_4]] +inline int* __counted_by(count) inline_header_ret_void_star_unsafe_indexable_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (void* __unsafe_indexable) 0; +} + +// These are the checks for the AST from the `bounds-attributed-in-return-null-system-header.c`. +// They have to be here because that's what FileCheck requires. + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_explicit_unspecified_cast_0:0x[^ ]+]] {{.+}} test_explicit_unspecified_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_explicit_unspecified_cast_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unspecified_cast_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_explicit_unspecified_cast_0(count)) <= inline_header_ret_explicit_unspecified_cast_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unspecified_cast_0(count)) - inline_header_ret_explicit_unspecified_cast_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_explicit_unsafe_indexable_cast_0:0x[^ ]+]] {{.+}} test_explicit_unsafe_indexable_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_explicit_unsafe_indexable_cast_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) <= inline_header_ret_explicit_unsafe_indexable_cast_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) - inline_header_ret_explicit_unsafe_indexable_cast_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_0:0x[^ ]+]] {{.+}} test_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_0(count)) <= inline_header_ret_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_0(count)) - inline_header_ret_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_void_star_unspecified_0:0x[^ ]+]] {{.+}} test_void_star_unspecified_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_void_star_unspecified_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unspecified_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_void_star_unspecified_0(count)) <= inline_header_ret_void_star_unspecified_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unspecified_0(count)) - inline_header_ret_void_star_unspecified_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_25]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}`-FunctionDecl [[func_test_void_star_unsafe_indexable_0:0x[^ ]+]] {{.+}} test_void_star_unsafe_indexable_0 +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_9:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-BoundsCheckExpr {{.+}} 'inline_header_ret_void_star_unsafe_indexable_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) <= inline_header_ret_void_star_unsafe_indexable_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) - inline_header_ret_void_star_unsafe_indexable_0(count) && 0 <= count' +// CHECK-NEXT: {{^}} |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_9]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_9]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} `-DeclRefExpr {{.+}} [[var_count_9]] diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c new file mode 100644 index 0000000000000..3472c95f7896e --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c @@ -0,0 +1,421 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +#include + +// CHECK: {{^}}|-FunctionDecl [[func_cb_0:0x[^ ]+]] {{.+}} cb_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len]] +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_NULL:0x[^ ]+]] {{.+}} cb_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_1]] +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_cast:0x[^ ]+]] {{.+}} cb_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_2]] +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_cast_NULL:0x[^ ]+]] {{.+}} cb_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_3]] +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} cb_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_4]] +int *__counted_by(len) cb_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_0:0x[^ ]+]] {{.+}} sb_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'size == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(size)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_size]] +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_cast:0x[^ ]+]] {{.+}} sb_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_5]] +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_cast_NULL:0x[^ ]+]] {{.+}} sb_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_6]] +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} sb_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_7]] +int *__sized_by(len) sb_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_0:0x[^ ]+]] {{.+}} cbn_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_8]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_cast:0x[^ ]+]] {{.+}} cbn_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_9]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_cast_NULL:0x[^ ]+]] {{.+}} cbn_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_10]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_23]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} cbn_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_11]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_0:0x[^ ]+]] {{.+}} sbn_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_12]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_int_cast:0x[^ ]+]] {{.+}} sbn_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_13]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_int_cast_NULL:0x[^ ]+]] {{.+}} sbn_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_14]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK: {{^}}`-FunctionDecl [[func_sbn_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} sbn_int_void_int_cast_NULL +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_len_15:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | `-ParenExpr +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}} | | `-ParenExpr +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_len_15]] +// CHECK-NEXT: {{^}} |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c new file mode 100644 index 0000000000000..78e92b9f26ad7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c @@ -0,0 +1,299 @@ + + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_cb_in_from_bidi:0x[^ ]+]] {{.+}} cb_in_from_bidi +// CHECK: |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count]] +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_indexable:0x[^ ]+]] {{.+}} cb_in_from_indexable +// CHECK: |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_1]] +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_single:0x[^ ]+]] {{.+}} cb_in_from_single +// CHECK: |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: `-OpaqueValueExpr [[ove_5]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_2]] +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_out_from_single:0x[^ ]+]] {{.+}} cb_out_from_single +// CHECK: |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && *count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= *count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: `-OpaqueValueExpr [[ove_7]] +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_3]] +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_sb_from_single:0x[^ ]+]] {{.+}} sb_from_single +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_8]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: `-OpaqueValueExpr [[ove_9]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size]] +void *__sized_by(size) sb_from_single(int size, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cbn_in_from_single:0x[^ ]+]] {{.+}} cbn_in_from_single +// CHECK: |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_10]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: `-OpaqueValueExpr [[ove_11]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_4]] +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_eb_in_from_single:0x[^ ]+]] {{.+}} eb_in_from_single +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'end <= __builtin_get_pointer_upper_bound(p) && p <= end' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single' +// CHECK: |-OpaqueValueExpr [[ove_12]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: `-OpaqueValueExpr [[ove_13]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_end]] +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} diff --git a/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..8251de52388ba --- /dev/null +++ b/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c @@ -0,0 +1,109 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -Wcast-qual 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -Wcast-qual 2>&1 | FileCheck %s +#include +void foo(void) { + char *dst, *src; + __builtin_memcpy(dst, src, 10); +} + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | |-VarDecl [[var_dst:0x[^ ]+]] +// CHECK: {{^}}| | `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}}| | | | `-AssumptionExpr +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | `-BoundsCheckExpr +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*)(void *__single __sized_by(), const void *__single __sized_by(), unsigned long)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by()':'const void *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: {{^}}| | | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: {{^}}| | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: {{^}}| | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' diff --git a/clang/test/BoundsSafety/AST/cast-inner-dimensions.c b/clang/test/BoundsSafety/AST/cast-inner-dimensions.c new file mode 100644 index 0000000000000..bc9f88452cd6b --- /dev/null +++ b/clang/test/BoundsSafety/AST/cast-inner-dimensions.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void foo(void) { + (void)(void *)(char *__single *)0; + (void)(char *__single *)(void *)0; +} + +// CHECK: TranslationUnitDecl +// CHECK: `-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: `-CompoundStmt +// CHECK: |-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__single*' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-CStyleCastExpr {{.+}} 'char *__single*' +// CHECK: `-CStyleCastExpr {{.+}} 'void *' +// CHECK: `-IntegerLiteral {{.+}} 0 diff --git a/clang/test/BoundsSafety/AST/common-sugared-type.c b/clang/test/BoundsSafety/AST/common-sugared-type.c new file mode 100644 index 0000000000000..2cc4119da29bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/common-sugared-type.c @@ -0,0 +1,41 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null + +struct s { + int dummy; +}; + +struct s *g; + +typedef struct s s_t; +s_t *f(void) { + s_t *l; + return g ? l : g; +} +// CHECK:TranslationUnitDecl {{.*}} <> +// CHECK:|-RecordDecl {{.*}} <{{.*}}common-sugared-type.c:9:1, line:11:1> line:9:8 struct s definition +// CHECK-NEXT:| `-FieldDecl {{.*}} col:6 dummy 'int' +// CHECK-NEXT:|-VarDecl {{.*}} col:11 used g 'struct s *__single' +// CHECK-NEXT:|-TypedefDecl {{.*}} col:18 referenced s_t 'struct s' +// CHECK-NEXT:| `-ElaboratedType {{.*}} 'struct s' sugar +// CHECK-NEXT:| `-RecordType {{.*}} 'struct s' +// CHECK-NEXT:| `-Record {{.*}} 's' +// CHECK-NEXT:`-FunctionDecl {{.*}} line:16:6 f 's_t *__single(void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:8 used l 's_t *__bidi_indexable' +// CHECK-NEXT: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 's_t *__single' +// CHECK-NEXT: `-ConditionalOperator {{.*}} 'struct s *__bidi_indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'struct s *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'struct s *__single' lvalue Var {{.*}} 'g' 'struct s *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 's_t *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 's_t *__bidi_indexable' lvalue Var {{.*}} 'l' 's_t *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'struct s *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'struct s *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'struct s *__single' lvalue Var {{.*}} 'g' 'struct s *__single' diff --git a/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c new file mode 100644 index 0000000000000..35355183988b3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c @@ -0,0 +1,189 @@ +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +// Tests the correctness of applying bounds attributes to complex type specifiers +// as well as to what extent other attributes (represented by _Nullable) are retained. + +#include "complex-typespecs-with-bounds.h" +#include + +void typeoftypes() { + typeof((long * _Nullable) 0) __single p1; + typeof(typeof(bar) *) __single p2; +} + +struct S { + char * _Nullable f1; +}; + +void typeofexprs(struct S s) { + typeof(foo()) __single p1; + typeof(&foo) __single p2; + typeof(&foo) __unsafe_indexable p3; // error: pointer cannot have more than one bound attribute + + typeof(bar) __single p4; + typeof(&bar) __single p5; // error: pointer cannot have more than one bound attribute + typeof(bar) * __single p6; + typeof(bar[2]) * __single p7; + typeof(&bar[2]) __single p8; + typeof(&*bar) __single p9; + + typeof(s.f1) __bidi_indexable p10; + typeof(*s.f1) * __bidi_indexable p11; + typeof(&*s.f1) __unsafe_indexable p12; +} + +typedef typeof(*bar) my_t; +typedef typeof(bar) my_ptr_t; +typedef typeof(*bar) * my_manual_ptr_t; + +void typedefs_of_typeof() { + my_t * __single p1; + my_ptr_t __single p2; + my_manual_ptr_t __single p3; + my_manual_ptr_t __bidi_indexable p4; + my_manual_ptr_t __unsafe_indexable p5; +} + +void autotypes(char * _Nullable __single p) { + __auto_type * __unsafe_indexable p1 = p; + __auto_type * __unsafe_indexable p2 = &*p; +} + +void typeofexpr_typeofexpr() { + typeof(bar) p1; + typeof(p1) __single p2; +} + +void typeofexpr_typeoftype_typeofexpr() { + typeof(typeof(bar)) p1; + typeof(p1) __single p2; +} + +void typeof_autotype1() { + __auto_type p1 = bar; + typeof(p1) __single p2; +} + +void typeof_autotype2() { + __auto_type * p1 = bar; + typeof(p1) __single p2; +} + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeoftypes:0x[^ ]+]] {{.+}} typeoftypes +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: |-RecordDecl +// CHECK: | `-FieldDecl +// CHECK: |-FunctionDecl [[func_typeofexprs:0x[^ ]+]] {{.+}} typeofexprs +// CHECK: | |-ParmVarDecl [[var_referenced:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p4:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p5:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p6:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p7:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p8:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p9:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p10:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p11:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p12:0x[^ ]+]] +// CHECK: |-TypedefDecl +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-BuiltinType +// CHECK: |-TypedefDecl +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-AttributedType +// CHECK: | `-AttributedType +// CHECK: | `-PointerType +// CHECK: | `-BuiltinType +// CHECK: |-TypedefDecl +// CHECK: | `-PointerType +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-BuiltinType +// CHECK: |-FunctionDecl [[func_typedefs_of_typeof:0x[^ ]+]] {{.+}} typedefs_of_typeof +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p4_1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p5_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_autotypes:0x[^ ]+]] {{.+}} autotypes +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1_2:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single _Nullable':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single _Nullable':'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: |-FunctionDecl [[func_typeofexpr_typeofexpr:0x[^ ]+]] {{.+}} typeofexpr_typeofexpr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeofexpr_typeoftype_typeofexpr:0x[^ ]+]] {{.+}} typeofexpr_typeoftype_typeofexpr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_2:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeof_autotype1:0x[^ ]+]] {{.+}} typeof_autotype1 +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_3:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_typeof_autotype2:0x[^ ]+]] {{.+}} typeof_autotype2 +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_referenced_4:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_6:0x[^ ]+]] + diff --git a/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h new file mode 100644 index 0000000000000..5659fcf83d793 --- /dev/null +++ b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h @@ -0,0 +1,4 @@ +#pragma clang system_header + +char * _Nullable foo(); +char * _Nullable bar; diff --git a/clang/test/BoundsSafety/AST/compound-assign-count.c b/clang/test/BoundsSafety/AST/compound-assign-count.c new file mode 100644 index 0000000000000..7c2748dd83479 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-assign-count.c @@ -0,0 +1,109 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety %s | FileCheck %s +#include + +struct T { + int *__counted_by(len) ptr; + int len; +}; + +void Test(struct T *t, int amt) { + t->ptr += amt; + t->len -= amt; +} + +// CHECK-LABEL: Test 'void (struct T *__single, int)' +// CHECK: |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_amt:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CompoundAssignOperator {{.+}} ComputeResultTy='int *__single +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_amt]] +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_amt]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MemberExpr {{.+}} ->len +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-CompoundAssignOperator {{.+}} 'int' '-=' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c new file mode 100644 index 0000000000000..e8b2771298e74 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c @@ -0,0 +1,1182 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cb { + int count; + char* __counted_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb_arr 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb (*__single)[]' +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cb_with_other_data_arr 'void (struct cb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb_with_other_data (*__single)[]' +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb 'struct cb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by((struct cb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cb 'void (struct cb *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb_from_cb 'struct cb (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cb 'void (char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by.c new file mode 100644 index 0000000000000..4e05530540e46 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by.c @@ -0,0 +1,4783 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cb { + int count; + char* __counted_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb_arr 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb (*__single)[]' +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cb_with_other_data_arr 'void (struct cb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb_with_other_data (*__single)[]' +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb 'struct cb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by((struct cb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && ptr->count <= __builtin_get_pointer_upper_bound(ptr->buf) - ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cb 'void (struct cb *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb_from_cb 'struct cb (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cb 'void (char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_null_ptr_cb 'void (int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: `-ParenExpr {{.+}} 'char *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_null_ptr_cb(int new_count) { + consume_cb((struct cb) { + .buf = (char*)0, + .count = new_count + }); + consume_cb((struct cb) { + .buf = (void*)0, + .count = new_count + }); + consume_cb((struct cb) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c new file mode 100644 index 0000000000000..f79187e4d7921 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c @@ -0,0 +1,1167 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon_arr 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon (*__single)[]' +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cbon_with_other_data_arr 'void (struct cbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon_with_other_data (*__single)[]' +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon 'struct cbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cbon 'void (struct cbon *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon_from_cbon 'struct cbon (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cbon 'void (char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c new file mode 100644 index 0000000000000..6bdbe1acedbef --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c @@ -0,0 +1,5129 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon_arr 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon (*__single)[]' +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cbon_with_other_data_arr 'void (struct cbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon_with_other_data (*__single)[]' +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon 'struct cbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by_or_null((struct cbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && !ptr->buf || ptr->count <= __builtin_get_pointer_upper_bound(ptr->buf) - ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cbon 'void (struct cbon *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon_from_cbon 'struct cbon (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cbon 'void (char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c new file mode 100644 index 0000000000000..7cb49c770f354 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c @@ -0,0 +1,957 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct eb { + char* __ended_by(end) start; + char* end; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb 'void (struct eb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct eb' +void consume_eb(struct eb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb_arr 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb (*__single)[]' +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_eb_with_other_data_arr 'void (struct eb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb_with_other_data (*__single)[]' +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb 'struct eb (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct eb_with_other_data *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_eb 'void (struct eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb_from_eb 'struct eb (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr_from_eb 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_eb 'void (struct eb_with_other_data *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_eb 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-ended_by.c b/clang/test/BoundsSafety/AST/compound-literal-ended_by.c new file mode 100644 index 0000000000000..4a620780b88b2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-ended_by.c @@ -0,0 +1,2532 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct eb { + char* __ended_by(end) start; + char* end; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb 'void (struct eb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct eb' +void consume_eb(struct eb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb_arr 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb (*__single)[]' +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_eb_with_other_data_arr 'void (struct eb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb_with_other_data (*__single)[]' +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb 'struct eb (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' '0 <= __builtin_get_pointer_upper_bound(0) && 0 <= 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + // Note the type of `.start` and `.end`'s assignments here looks odd in the + // AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single /* __started_by((struct eb[2]){{.start = new_start, .end = new_end}, {.start = 0, .end = 0}}[1UL].start) */ ':'char *__single' + // + // We could consider constant folding the expression + // to `__ended_by(0)` but this doesn't seem to be necessary. + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct eb_with_other_data *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_eb 'void (struct eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb_from_eb 'struct eb (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr_from_eb 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' '0 <= __builtin_get_pointer_upper_bound(0) && 0 <= 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + // Note the type of `.start` and `.end`'s assignments here looks odd in the + // AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single /* __started_by((struct eb[2]){{.start = new_start, .end = new_end}, {.start = 0, .end = 0}}[1UL].start) */ ':'char *__single' + // + // We could consider constant folding the expression + // to `__ended_by(0)` but this doesn't seem to be necessary. + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_eb 'void (struct eb_with_other_data *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_eb 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-pointer.c b/clang/test/BoundsSafety/AST/compound-literal-pointer.c new file mode 100644 index 0000000000000..afd88df28033f --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-pointer.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include +#include "compound-literal-pointer.h" + +// CHECK: |-FunctionDecl {{.+}} return_ptr_nonptr +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_nonptr(void) { + return (int *){ 0 }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_single +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__single' lvalue +int *return_ptr_single(int *__single p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_indexable +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__indexable' lvalue +int *return_ptr_indexable(int *__indexable p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_bidi_indexable +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_bidi_indexable(int *__bidi_indexable p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_counted_by +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_counted_by(int *__counted_by(n) p, int n) { + return (int *){ p }; +} + +// CHECK: `-FunctionDecl {{.+}} return_ptr_unsafe_indexable +// CHECK: `-CompoundLiteralExpr {{.+}} 'int *__unsafe_indexable' lvalue +int *__unsafe_indexable return_ptr_unsafe_indexable(int *__unsafe_indexable p) { + return (int *){ p }; +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-pointer.h b/clang/test/BoundsSafety/AST/compound-literal-pointer.h new file mode 100644 index 0000000000000..d7de4fc9384c7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-pointer.h @@ -0,0 +1,27 @@ +#pragma clang system_header + +// CHECK: |-FunctionDecl {{.+}} compound_from_argument +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_argument(int *p) { + return (int *) { p }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_addrof +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_addrof(void) { + int x; + return (int *) { &x }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_null +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_null(void) { + int x; + return (int *) { 0 }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_function_call +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_function_call(void) { + return (int *) { compound_from_null() }; +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c new file mode 100644 index 0000000000000..2efe8c7885ed5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c @@ -0,0 +1,1174 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sb { + int count; + char* __sized_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb_arr 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb (*__single)[]' +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sb_with_other_data_arr 'void (struct sb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb_with_other_data (*__single)[]' +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb 'struct sb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by((struct sb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sb 'void (struct sb *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb_from_sb 'struct sb (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sb 'void (char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by.c new file mode 100644 index 0000000000000..4e583069aa98d --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by.c @@ -0,0 +1,4736 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sb { + int count; + char* __sized_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb_arr 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb (*__single)[]' +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sb_with_other_data_arr 'void (struct sb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb_with_other_data (*__single)[]' +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb 'struct sb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by((struct sb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && ptr->count <= (char *)__builtin_get_pointer_upper_bound(ptr->buf) - (char *__bidi_indexable)ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sb 'void (struct sb *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb_from_sb 'struct sb (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sb 'void (char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c new file mode 100644 index 0000000000000..753aaddf32f3a --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c @@ -0,0 +1,1181 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon_arr 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon (*__single)[]' +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sbon_with_other_data_arr 'void (struct sbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon_with_other_data (*__single)[]' +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon 'struct sbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by_or_null((struct sbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sbon 'void (struct sbon *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon_from_sbon 'struct sbon (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sbon 'void (char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c new file mode 100644 index 0000000000000..c71b29e049eb2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c @@ -0,0 +1,5184 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon_arr 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon (*__single)[]' +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sbon_with_other_data_arr 'void (struct sbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon_with_other_data (*__single)[]' +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon 'struct sbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by_or_null((struct sbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && !ptr->buf || ptr->count <= (char *)__builtin_get_pointer_upper_bound(ptr->buf) - (char *__bidi_indexable)ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sbon 'void (struct sbon *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon_from_sbon 'struct sbon (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sbon 'void (char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c b/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c new file mode 100644 index 0000000000000..0c032829faafc --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c @@ -0,0 +1,327 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void TestCountedPtr(int *__counted_by(*len) *ptr, unsigned *len) { + *ptr += 4; + *len = *len - 4; +} + +// CHECK-LABEL: TestCountedPtr +// CHECK: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='int *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 4 +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' + +void TestCount(int *__counted_by(len) ptr, unsigned len) { + int len2 = len; + int *__counted_by(len2) ptr2 = ptr; + + ptr2 = ptr2; + len2 /= 4; +} +// CHECK-LABEL: TestCount +// CHECK: | |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_ptr2:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'unsigned int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'unsigned int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len2)':'int *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | `-IntegerLiteral {{.+}} 4 +// CHECK: | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '/' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} 'int' '/=' +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' + +struct EndedBy { + int *__ended_by(end) start; + char *end; +}; +void TestRange(struct EndedBy *e) { + e->end -= 4; + e->start = e->start; +} +// CHECK-LABEL: TestRange +// CHECK: |-ParmVarDecl [[var_e:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CompoundAssignOperator {{.+}} */ ':' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_21]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_22]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-MemberExpr {{.+}} ->start +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | |-OpaqueValueExpr [[ove_24]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | `-OpaqueValueExpr [[ove_23]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'struct EndedBy *__single' +// CHECK: | |-OpaqueValueExpr [[ove_24]] {{.*}} 'struct EndedBy *__single' +// CHECK: | `-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->start +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_19]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/const-cast.c b/clang/test/BoundsSafety/AST/const-cast.c new file mode 100644 index 0000000000000..8a9f31e307383 --- /dev/null +++ b/clang/test/BoundsSafety/AST/const-cast.c @@ -0,0 +1,222 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-incompatible-pointer-types %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O2 %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-incompatible-pointer-types %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O2 %s -o /dev/null + +#include + +void implicit(void) { + int *__indexable i; + const int *__indexable ci; + const int *__bidi_indexable cbi; + const void *__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + i = ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + ci = i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + i = cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbi = i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + i = cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbv = i; +} + +void implicit_nested(void) { + int **__indexable i; + const int **__indexable ci; + const int **__bidi_indexable cbi; + const void **__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + i = ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + ci = i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + i = cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbi = i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + i = cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbv = i; +} + +void explicit(void) { + int *__indexable i; + const int *__indexable ci; + const int *__bidi_indexable cbi; + const void *__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + i = (int *__indexable)ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + ci = (const int *__indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + i = (int *__indexable)cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbi = (const int *__bidi_indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + i = (int *__indexable)cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbv = (const void *__bidi_indexable)i; +} + +void explicit_nested(void) { + int **__indexable i; + const int **__indexable ci; + const int **__bidi_indexable cbi; + const void **__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + i = (int **__indexable)ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + ci = (const int **__indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + i = (int **__indexable)cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbi = (const int **__bidi_indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + i = (int **__indexable)cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbv = (const void **__bidi_indexable)i; +} diff --git a/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c b/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c new file mode 100644 index 0000000000000..6764e339afc36 --- /dev/null +++ b/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c @@ -0,0 +1,77 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s 2>&1 | FileCheck %s + +#include + +struct Foo { + void *__sized_by(sizeof(int) * num_elements) vptr; + unsigned int num_elements; + + unsigned long long number_of_bits; + const unsigned char *__sized_by((number_of_bits + 7) & -7) buffer; + + char *__sized_by(sizeof(char) * 100) cptr; +}; + +// CHECK: RecordDecl {{.*}} struct Foo definition +// CHECK: |-FieldDecl [[VPTR_PTR:0x[0-9a-z]*]] {{.*}} vptr 'void *__single __sized_by(4UL * num_elements)':'void *__single' +// CHECK: |-FieldDecl {{.*}} referenced num_elements 'unsigned int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[VPTR_PTR]] 0 +// CHECK: |-FieldDecl {{.*}} referenced number_of_bits 'unsigned long long' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[BUFFER_PTR:0x[0-9a-z]*]] 0 +// CHECK: |-FieldDecl [[BUFFER_PTR]] {{.*}} buffer 'const unsigned char *__single __sized_by((number_of_bits + 7) & -7)':'const unsigned char *__single' +// CHECK: `-FieldDecl {{.*}} cptr 'char *__single __sized_by(100UL)':'char *__single' + +static int g_arr[] = { 0, 1, 2 }; // expected-note{{'g_arr' declared here}} +static char g_char[100]; +const unsigned char g_cchar[] = "Hello world!"; // expected-note{{'g_cchar' declared here}} +// confirms that the compiler treats this as compile-time constant. +struct Foo g_foo = {g_arr, sizeof(g_arr)/sizeof(int), 3, g_cchar, g_char}; +// expected-error@+1{{initializing 'g_foo_ovf.buffer' of type 'const unsigned char *__single __sized_by((number_of_bits + 7) & -7)' (aka 'const unsigned char *__single') and size value of 17 with array 'g_cchar' (which has 13 bytes) always fails}} +struct Foo g_foo_ovf = {g_arr, sizeof(g_arr)/sizeof(int), 10, g_cchar, g_char}; + +struct Bar { + int num; + int *__counted_by(*(&num) + *&(*&num) + (-num)) iptr; +}; + +const float cf = 1.0f; +struct Baz { + float f; + int *__counted_by((int)f) ptr1; + // FIXME: "*&" pattern with '&' wrapped in C-style casts is not supported yet. + int *__counted_by(*(int*)(&f)) ptr2; // expected-error{{invalid argument expression to bounds attribute}} + int *__counted_by(*(int*)(&cf)) ptr3; // expected-error{{invalid argument expression to bounds attribute}} +}; + +struct Bar g_bar = {3, g_arr}; +// expected-error@+1{{initializing 'g_bar_ovf.iptr' of type 'int *__single __counted_by(num + num + (-num))' (aka 'int *__single') and count value of 4 with array 'g_arr' (which has 3 elements) always fails}} +struct Bar g_bar_ovf = {4, g_arr}; + +void test() { + int arr[100]; + int *var; + void *__sized_by(sizeof(var) * 10) vptr = arr; + + const int num = 10; + int *__counted_by(*(&num) + *&(*&num)) iptr = arr; + int *__counted_by((int)10.0f) iptr2 = arr; + int *__counted_by(10.0f) iptr3 = arr; // expected-error{{'__counted_by' attribute requires an integer type argument}} + + iptr = iptr2; // this is not an error since 'num' is constant. +} + +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} referenced var 'int *__bidi_indexable' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} vptr 'void *__single __sized_by(240UL)':'void *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} used num 'const int' cinit +// CHECK: `-IntegerLiteral {{.*}} 'int' 10 +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr 'int *__single __counted_by(20)':'int *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr2 'int *__single __counted_by(10)':'int *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr3 'int *__single __counted_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c b/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c new file mode 100644 index 0000000000000..2727f2280f394 --- /dev/null +++ b/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c @@ -0,0 +1,202 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// Check the GNU extension to the conditional operator. + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: `-ParmVarDecl [[var_p:0x[^ ]+]] {{.+}} p 'void *__single __sized_by(16)':'void *__single' +void foo(void *__sized_by(16) p); + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(16))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(16)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryConditionalOperator {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | |-CStyleCastExpr {{.+}} 'void *' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +void bar(void) { + unsigned char buf[16]; + foo((void *)0 ?: buf); +} + +// CHECK: FunctionDecl [[func_returns_a_pointer:0x[^ ]+]] {{.+}} returns_a_pointer +void *__bidi_indexable returns_a_pointer(void) { + return 0; +} + +// CHECK: FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(16))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(16)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-BinaryConditionalOperator {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable(*__single)(void)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_returns_a_pointer]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +void baz(void) { + foo(returns_a_pointer() ?: 0); +} + +int bounds_safety_func(int * __counted_by(b) a, int b); +// CHECK: FunctionDecl [[func_bounds_safety_func:0x[^ ]+]] {{.+}} bounds_safety_func +// CHECK: |-ParmVarDecl [[var_a:0x[^ ]+]] +// CHECK: `-ParmVarDecl [[var_b:0x[^ ]+]] +// CHECK: `-DependerDeclsAttr + +int eval_count_arg(int * __bidi_indexable a, int b) { + return bounds_safety_func(a, b ?: 0); +} + +// CHECK: FunctionDecl [[func_eval_count_arg:0x[^ ]+]] {{.+}} eval_count_arg +// CHECK: |-ParmVarDecl [[var_a_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'a <= __builtin_get_pointer_upper_bound(a) && __builtin_get_pointer_lower_bound(a) <= a && b ?: 0 <= __builtin_get_pointer_upper_bound(a) - a && 0 <= b ?: 0' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int (*__single)(int *__single __counted_by(b), int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_bounds_safety_func]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(b)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_a_1]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-BinaryConditionalOperator {{.+}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_b_1]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' + diff --git a/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c b/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c new file mode 100644 index 0000000000000..0d868968776d1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c @@ -0,0 +1,86 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -triple arm64-apple-ios -target-feature +sve 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -target-feature +sve %s 2>&1 | FileCheck %s + +typedef struct opaque_s *opaque_t; + +// CHECK: VarDecl {{.+}} g_void 'void *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1] +void *g_void = (void *)(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_opaque 'struct opaque_s *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct opaque_s *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct opaque_s *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +opaque_t g_opaque = (opaque_t)(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_func 'void (*__single)(int)' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(int)' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void (*__bidi_indexable)(int)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +void (*g_func)(int) = (void (*)(int))(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_sizeless '__SVInt8_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} '__SVInt8_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} '__SVInt8_t *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +__SVInt8_t *g_sizeless = (__SVInt8_t *)(unsigned char[1]){}; + +int f_void(void *); +int f_opaque(opaque_t); +int f_func(void (*)(int)); +int f_sizeless(__SVInt8_t *); + +void foo(void) { + int result; + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(void *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void *__single)' Function {{.+}} 'f_void' 'int (void *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_void((void *)(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(struct opaque_s *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (struct opaque_s *__single)' Function {{.+}} 'f_opaque' 'int (struct opaque_s *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct opaque_s *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct opaque_s *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_opaque((opaque_t)(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(void (*__single)(int))' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void (*__single)(int))' Function {{.+}} 'f_func' 'int (void (*__single)(int))' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(int)' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void (*__bidi_indexable)(int)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_func((void (*)(int))(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(__SVInt8_t *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (__SVInt8_t *__single)' Function {{.+}} 'f_sizeless' 'int (__SVInt8_t *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} '__SVInt8_t *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} '__SVInt8_t *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_sizeless((__SVInt8_t *)(unsigned char[1]){}); +} diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c b/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c new file mode 100644 index 0000000000000..9fda321d41397 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c @@ -0,0 +1,89 @@ + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK-NOT: BoundsCheckExpr +// CHECK-NOT: BoundsSafetyPointerPromotionExpr +// CHECK-NOT: MaterializeSequenceExpr + +// CHECK: FunctionDecl {{.+}} foo 'void (int * __counted_by(count), int)' +void foo(int *__counted_by(count) p, int count) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + (void)*p; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)p[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __counted_by(count)':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __counted_by(count)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + p = p + 42; + count = count - 42; +} + +// CHECK: RecordDecl {{.+}} struct bar definition +// CHECK: |-FieldDecl {{.+}} q 'int * __sized_by(size)':'int *' +// CHECK-NEXT: `-FieldDecl {{.+}} referenced size 'int' +struct bar { + int *__sized_by(size) q; + int size; +}; + +// CHECK: FunctionDecl {{.+}} baz 'void (struct bar *)' +void baz(struct bar *b) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + (void)*b->q; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)b->q[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __sized_by(size)':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __sized_by(size)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int' lvalue ->size + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int' lvalue ->size + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + b->q = b->q + 42; + b->size = b->size - 42; +} diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-func.c b/clang/test/BoundsSafety/AST/count-attributed-type-func.c new file mode 100644 index 0000000000000..af3f9690b807a --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-func.c @@ -0,0 +1,68 @@ + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl {{.+}} cb_in_in 'void (int *{{.*}}__counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void cb_in_in(int *__counted_by(count) ptr, int count); + +// CHECK: FunctionDecl {{.+}} cb_in_out 'void (int *{{.*}}__counted_by(*count), int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(*count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int *__single' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 0 +void cb_in_out(int *__counted_by(*count) ptr, int *__single count); + +// CHECK: FunctionDecl {{.+}} cb_out_in 'void (int *{{.*}}__counted_by(count)*__single, int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)*__single' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 1 +void cb_out_in(int *__counted_by(count) *__single ptr, int count); + +// CHECK: FunctionDecl {{.+}} cb_out_out 'void (int *{{.*}}__counted_by(*count)*__single, int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(*count)*__single' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int *__single' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 1 +void cb_out_out(int *__counted_by(*count) *__single ptr, int *__single count); + +// CHECK: FunctionDecl {{.+}} cbn 'void (int *{{.*}}__counted_by_or_null(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by_or_null(count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void cbn(int *__counted_by_or_null(count) ptr, int count); + +// CHECK: FunctionDecl {{.+}} sb 'void (void *{{.*}}__sized_by(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'void *{{.*}}__sized_by(size)':'void *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void sb(void *__sized_by(size) ptr, int size); + +// CHECK: FunctionDecl {{.+}} sbn 'void (void *{{.*}}__sized_by_or_null(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'void *{{.*}}__sized_by_or_null(size)':'void *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void sbn(void *__sized_by_or_null(size) ptr, int size); + +// CHECK: FunctionDecl {{.+}} rcb 'int *{{.*}}__counted_by(count)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +int *__counted_by(count) rcb(int count); + +// CHECK: FunctionDecl {{.+}} rcbn 'int *{{.*}}__counted_by_or_null(count)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +int *__counted_by_or_null(count) rcbn(int count); + +// CHECK: FunctionDecl {{.+}} rsb 'void *{{.*}}__sized_by(size)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +void *__sized_by(size) rsb(int size); + +// CHECK: FunctionDecl {{.+}} rsbn 'void *{{.*}}__sized_by_or_null(size)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +void *__sized_by_or_null(size) rsbn(int size); diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c b/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c new file mode 100644 index 0000000000000..53aed9f7ce187 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c @@ -0,0 +1,34 @@ + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +// This test ensures that the parsing is done late enough to have a complete +// struct type and be able to use offsetof()/sizeof(). + +// CHECK: RecordDecl {{.+}} struct foo +// CHECK: FieldDecl {{.+}} dummy 'int' +// CHECK-NEXT: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(0UL)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} ptr2 'int *{{.*}}__counted_by(24UL)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} dummy2 'int' +struct foo { + int dummy; + int *__counted_by(offsetof(struct foo, dummy)) ptr; + int *__counted_by(offsetof(struct foo, dummy2)) ptr2; + int dummy2; +}; + +// CHECK: RecordDecl {{.+}} struct bar +// CHECK: FieldDecl {{.+}} dummy 'int' +// CHECK-NEXT: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(16UL)':'int *{{.*}}' +struct bar { + int dummy; + int *__counted_by(sizeof(struct bar)) ptr; +}; diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-struct.c b/clang/test/BoundsSafety/AST/count-attributed-type-struct.c new file mode 100644 index 0000000000000..8fe42c1b7b8e5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-struct.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct cb { + int *__counted_by(count) ptr; + int count; +}; + +// CHECK: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by_or_null(count)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct cbn { + int *__counted_by_or_null(count) ptr; + int count; +}; + +// CHECK: FieldDecl {{.+}} ptr 'void *{{.*}}__sized_by(size)':'void *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct sb { + void *__sized_by(size) ptr; + int size; +}; + +// CHECK: FieldDecl {{.+}} ptr 'void *{{.*}}__sized_by_or_null(size)':'void *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct sbn { + void *__sized_by_or_null(size) ptr; + int size; +}; diff --git a/clang/test/BoundsSafety/AST/count-attrs.c b/clang/test/BoundsSafety/AST/count-attrs.c new file mode 100644 index 0000000000000..76e83bd01441f --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attrs.c @@ -0,0 +1,117 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int *__counted_by(len) frob(int len); + +int *__sized_by(len) byte_frob(int len); +void *alloc_bytes(int byte_count) __attribute__((alloc_size(1))); +// +// void *alloc_items(int byte_count, int size) __attribute__((alloc_size(1, 2))); + +typedef int (__array_decay_discards_count_in_parameters int_array_t)[10]; +void count_attr_in_bracket(int buf[__counted_by(len)], int len); +void count_ignored_from_array(int (__array_decay_discards_count_in_parameters buf)[10]); +void count_ignored_and_attr(int_array_t __counted_by(count) buf, int count); + +struct s { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + unsigned char *bp3 __sized_by(l); + unsigned char *bp4 __sized_by(l+1); + int l; +}; + +void test(void) { + int n = 0; + int *__counted_by(n) buf1; + int n2 = 0; + int *buf2 __counted_by(n2); + int n3 = sizeof(int) * n; + unsigned char *__sized_by(n3) byte_buf1; + int n4 = sizeof(int) * n2; + unsigned char *byte_buf2 __sized_by(n4); +} + +int *__counted_by(len) frob_body(int len) { return 0; } +int *__sized_by(len) byte_frob_body(int len) { return 0; } +// CHECK:TranslationUnitDecl {{.*}} +// CHECK:|-FunctionDecl {{.*}} frob 'int *__single __counted_by(len)(int)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} byte_frob 'int *__single __sized_by(len)(int)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} alloc_bytes 'void *__single(int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} byte_count 'int' +// CHECK-NEXT:| `-AllocSizeAttr {{.*}} 1 +// CHECK-NEXT:|-TypedefDecl {{.*}} referenced int_array_t '__array_decay_discards_count_in_parameters int[10]':'int[10]' +// CHECK-NEXT:| `-MacroQualifiedType {{.*}} '__array_decay_discards_count_in_parameters int[10]' sugar +// CHECK-NEXT:| `-AttributedType {{.*}} 'int[10] __attribute__((decay_discards_count_in_parameters))' sugar +// CHECK-NEXT:| `-ParenType {{.*}} 'int[10]' sugar +// CHECK-NEXT:| `-ConstantArrayType {{.*}} 'int[10]' 10 +// CHECK-NEXT:| `-BuiltinType {{.*}} 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} count_attr_in_bracket 'void (int *__single __counted_by(len), int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:|-FunctionDecl {{.*}} count_ignored_from_array 'void (int *__single)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} buf 'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} count_ignored_and_attr 'void (int *__single __counted_by(count), int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used count 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:|-RecordDecl {{.*}} struct s definition +// CHECK-NEXT:| |-FieldDecl {{.*}} bp 'int *__single __counted_by(l)':'int *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp2 'int *__single __counted_by(l + 1)':'int *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp3 'unsigned char *__single __sized_by(l)':'unsigned char *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp4 'unsigned char *__single __sized_by(l + 1)':'unsigned char *__single' +// CHECK-NEXT:| `-FieldDecl {{.*}} referenced l 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} {{.*}} {{.*}} {{.*}} 0 0 0 0 +// CHECK-NEXT:|-FunctionDecl {{.*}} test 'void (void)' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} buf1 'int *__single __counted_by(n)':'int *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n2 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} buf2 'int *__single __counted_by(n2)':'int *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n3 'int' cinit +// CHECK-NEXT:| | |-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-BinaryOperator {{.*}} 'unsigned long' '*' +// CHECK-NEXT:| | | |-UnaryExprOrTypeTraitExpr {{.*}} 'unsigned long' sizeof 'int' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'unsigned long' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'n' 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} byte_buf1 'unsigned char *__single __sized_by(n3)':'unsigned char *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n4 'int' cinit +// CHECK-NEXT:| | |-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-BinaryOperator {{.*}} 'unsigned long' '*' +// CHECK-NEXT:| | | |-UnaryExprOrTypeTraitExpr {{.*}} 'unsigned long' sizeof 'int' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'unsigned long' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'n2' 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| `-DeclStmt {{.*}} +// CHECK-NEXT:| `-VarDecl {{.*}} byte_buf2 'unsigned char *__single __sized_by(n4)':'unsigned char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} frob_body 'int *__single __counted_by(len)(int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| `-ReturnStmt {{.*}} +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:`-FunctionDecl {{.*}} byte_frob_body 'int *__single __sized_by(len)(int)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 diff --git a/clang/test/BoundsSafety/AST/count-init-list.c b/clang/test/BoundsSafety/AST/count-init-list.c new file mode 100644 index 0000000000000..65b7ccf4f018f --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-init-list.c @@ -0,0 +1,56 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +void Test(void) { + int *p; + struct Foo f = { 10, p }; +} + +// CHECK-LABEL: Test 'void (void)' +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-InitListExpr +// CHECK: | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p]] diff --git a/clang/test/BoundsSafety/AST/count-return.c b/clang/test/BoundsSafety/AST/count-return.c new file mode 100644 index 0000000000000..7e5a5500f63be --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-return.c @@ -0,0 +1,104 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +int *__sized_by(len) alloc(int len); +// CHECK: FunctionDecl [[func_alloc:0x[^ ]+]] {{.+}} alloc + +int *__counted_by(4) noproto(); // non-prototype function +// CHECK: FunctionDecl [[func_noproto:0x[^ ]+]] {{.+}} noproto 'int *__single __counted_by(4)()' + +int Test() { + int len = 16; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len_1:0x[^ ]+]] + + int *bufAuto = alloc(len); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufAuto:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufBound:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | | | |-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *buf4 = noproto(); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_buf4:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(4)(*__single)()' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_noproto]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 4 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + + return buf4[3] + bufBound[9]; +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-ArraySubscriptExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_buf4]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 3 +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-ArraySubscriptExpr +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_bufBound]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 9 +} diff --git a/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c b/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c new file mode 100644 index 0000000000000..93611ddb4a0a6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c @@ -0,0 +1,209 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'x + 1UL <= __builtin_get_pointer_upper_bound(x + 1UL) && __builtin_get_pointer_lower_bound(x + 1UL) <= x + 1UL && count - 1U <= __builtin_get_pointer_upper_bound(x + 1UL) - x + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_x]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' + +// WITHOUT: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// WITHOUT-NEXT: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// WITHOUT-NEXT: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// WITHOUT-NEXT: | | `-DependerDeclsAttr +// WITHOUT-NEXT: | `-CompoundStmt +// WITHOUT-NEXT: | `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT-NEXT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT-NEXT: | | `-UnaryOperator {{.+}} postfix '++' +// WITHOUT-NEXT: | | `-DeclRefExpr {{.+}} [[var_x]] +// WITHOUT-NEXT: | `-IntegerLiteral {{.+}} 0 +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; + #endif +} + +// CHECK: {{^}}`-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_x_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-DependerDeclsAttr +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} 'x + 1 <= __builtin_get_pointer_upper_bound(x + 1) && __builtin_get_pointer_lower_bound(x + 1) <= x + 1 && count - 1 <= __builtin_get_pointer_upper_bound(x + 1) - x + 1 && 0 <= count - 1' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | | |-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}} | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 1 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 +// CHECK-NEXT: {{^}} `-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}} | `-ParenExpr +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}} | | |-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} `-IntegerLiteral {{.+}} 0 + +// WITHOUT-NEXT: {{^}}`-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// WITHOUT-NEXT: {{^}} |-ParmVarDecl [[var_x_1:0x[^ ]+]] +// WITHOUT-NEXT: {{^}} |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// WITHOUT-NEXT: {{^}} | `-DependerDeclsAttr +// WITHOUT-NEXT: {{^}} `-CompoundStmt +// WITHOUT-NEXT: {{^}} `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT-NEXT: {{^}} |-UnaryOperator {{.+}} cannot overflow +// WITHOUT-NEXT: {{^}} | `-ParenExpr +// WITHOUT-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// WITHOUT-NEXT: {{^}} | |-DeclRefExpr {{.+}} [[var_x_1]] +// WITHOUT-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// WITHOUT-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// WITHOUT-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// WITHOUT-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// WITHOUT-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// WITHOUT-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// WITHOUT: {{^}} | | | |-OpaqueValueExpr [[ove]] +// WITHOUT-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_x_1]] +// WITHOUT-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_1]] +// WITHOUT-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// WITHOUT-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// WITHOUT-NEXT: {{^}} | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// WITHOUT: {{^}} | `-IntegerLiteral {{.+}} 1 +// WITHOUT-NEXT: {{^}} `-IntegerLiteral {{.+}} 0 + +void bar(int *__counted_by(count) x, int count) { + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; + #endif + *(x = x+1) = 0; +} diff --git a/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c b/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c new file mode 100644 index 0000000000000..6248311745767 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include +#include + +struct Foo { + int len; + int fam[__counted_by((len - offsetof(struct Foo, fam)) / sizeof(int))]; +}; + +// CHECK: -RecordDecl {{.*}} struct Foo definition +// CHECK-NEXT: |-FieldDecl {{.*}} referenced len 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT: `-FieldDecl {{.*}} fam 'int[__counted_by((len - 4UL) / 4UL)]':'int[]' + +struct Bar { + int len; + int fam[__counted_by((len - sizeof(struct Foo)) / sizeof(int))]; +}; + +// CHECK: -RecordDecl {{.*}} struct Bar definition +// CHECK-NEXT: |-FieldDecl {{.*}} referenced len 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT: `-FieldDecl {{.*}} fam 'int[__counted_by((len - 4UL) / 4UL)]':'int[]' diff --git a/clang/test/BoundsSafety/AST/counted_by_const_call.c b/clang/test/BoundsSafety/AST/counted_by_const_call.c new file mode 100644 index 0000000000000..1a579e702264a --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_const_call.c @@ -0,0 +1,151 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int data_const_count __unsafe_late_const; + +// CHECK: |-FunctionDecl [[func_fun_const_count:0x[^ ]+]] {{.+}} fun_const_count +__attribute__((const)) int fun_const_count() { + return data_const_count; +} + +struct struct_const_count_call { + int *__counted_by(fun_const_count()) ptr; +}; + +// CHECK-LABEL: fun_pointer_access +void fun_pointer_access(struct struct_const_count_call *sp) { + *(sp->ptr) = 0; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count_call *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_fun_const_count]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_const_count_call *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count_call *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_fun_const_count]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// FIXME: rdar://85158790 +// CHECK-LABEL: fun_struct_noinit +void fun_struct_noinit() { + struct struct_const_count_call s; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s:0x[^ ]+]] + +// CHECK-LABEL: fun_struct_init +void fun_struct_init(int *__bidi_indexable buf) { + struct struct_const_count_call s = { buf }; +} +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-InitListExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_4]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: `-OpaqueValueExpr [[ove_5]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-CallExpr +// CHECK: `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: `-DeclRefExpr {{.+}} [[func_fun_const_count]] + diff --git a/clang/test/BoundsSafety/AST/counted_by_const_count_init.c b/clang/test/BoundsSafety/AST/counted_by_const_count_init.c new file mode 100644 index 0000000000000..d405f84eb6310 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_const_count_init.c @@ -0,0 +1,104 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct struct_const_count { + unsigned const const_count; + int *__counted_by(const_count) ptr; +}; + +enum enum_count { en_count = 10 }; +// CHECK-LABEL: fun_enum_count +void fun_enum_count(int *__sized_by(en_count) ptr, int *__bidi_indexable buf) { + ptr = buf; +} +// CHECK: | |-ParmVarDecl {{.*}} 'int *__single __sized_by(10)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: fun_pointer_access +void fun_pointer_access(struct struct_const_count *sp) { + *(sp->ptr) = 0; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const unsigned int' +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'const unsigned int' +// CHECK: | | | | `-MemberExpr {{.+}} ->const_count +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'const unsigned int' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(const_count)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-MemberExpr {{.+}} ->const_count +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// FIXME: rdar://85158790 +// CHECK-LABEL: fun_struct_noinit +void fun_struct_noinit() { + struct struct_const_count s; +} +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] diff --git a/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c b/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c new file mode 100644 index 0000000000000..8a6d221cbedf2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c @@ -0,0 +1,197 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +// CHECK: {{^}}|-VarDecl [[var_data_const_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-UnsafeLateConstAttr +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +unsigned data_const_count __unsafe_late_const; + +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| `-FieldDecl +struct struct_data_const_count { + int *__counted_by(data_const_count) ptr; +}; + +// CHECK: {{^}}|-VarDecl [[var_data_const_count_flex:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-UnsafeLateConstAttr +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +unsigned data_const_count_flex __unsafe_late_const; + +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| `-FieldDecl +struct struct_data_const_count_flex { + int count; + int fam[__counted_by(data_const_count_flex)]; +}; + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_data_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// CHECK-LABEL: fun_pointer_assignment2 +void fun_pointer_assignment2(struct struct_data_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; + data_const_count = 10; // XXX: The assignment precheck at `buf` won't take into account this new count assignment. +} +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsCheckExpr {{.+}} 'buf <= __builtin_get_pointer_upper_bound(buf) && __builtin_get_pointer_lower_bound(buf) <= buf && data_const_count <= __builtin_get_pointer_upper_bound(buf) - buf' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count *__single' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 10 + + +// CHECK-LABEL: fun_flex_pointer_assignment +void fun_flex_pointer_assignment(struct struct_data_const_count_flex *sp, void *__bidi_indexable buf) { + sp = buf; +} +// CHECK: |-ParmVarDecl [[var_sp_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct struct_data_const_count_flex *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_sp_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + +// CHECK-LABEL: fun_flex_pointer_assignment2 +void fun_flex_pointer_assignment2(struct struct_data_const_count_flex *sp, void *__bidi_indexable buf) { + sp = buf; + data_const_count_flex = 100; // XXX: The assignment precheck at `buf` won't take into account this new count assignment. +} +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_sp_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'struct struct_data_const_count_flex *__single' '=' +// CHECK-NEXT: {{^}} | | | |-DeclRefExpr {{.+}} [[var_sp_3]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__single' +// CHECK-NEXT: {{^}} | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK-NEXT: {{^}} | | |-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} | `-IntegerLiteral {{.+}} 100 +// CHECK-NEXT: {{^}} `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} `-IntegerLiteral {{.+}} 100 diff --git a/clang/test/BoundsSafety/AST/counted_by_global_assign.c b/clang/test/BoundsSafety/AST/counted_by_global_assign.c new file mode 100644 index 0000000000000..cbe2bc66e0e6d --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_global_assign.c @@ -0,0 +1,62 @@ + +// FileCheck doesn't seem to be able to handle too many slashes in a line +// XFAIL: * + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int glen; +int *__counted_by(glen) gptr; +// CHECK: |-VarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^ *}}DependerDeclsAttr +// CHECK: |-VarDecl [[var_ptr:0x[^ ]+]] + +// CHECK-LABEL: test +void test(int *__bidi_indexable arg) { + glen = 0; + gptr = arg; +} +// CHECK: |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arg]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c b/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c new file mode 100644 index 0000000000000..84853a7fb159d --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c @@ -0,0 +1,616 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// First make sure the previously buggy diagnostic is present +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s + + +// Now make sure the AST is as expected (no errors) and identical with and without the new bounds checks +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count -ast-dump %s 2>&1 | FileCheck %s + + +// Verify we generate the same AST even when the warning is treated as an error, with and without the new bounds checks +// Note: `-verify=default` is needed here, otherwise clang will return a non-zero exit because of the error. +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify=default -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify=default -ast-dump %s 2>&1 | FileCheck %s + +#include +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:{{.+}}, line:{{.+}}> line:{{.+}} test_cb_unary 'void (int *__single __counted_by(3))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used p 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && 3 <= __builtin_get_pointer_upper_bound(p + 1UL) - p + 1UL && 0 <= 3' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int *__single __counted_by(3)':'int *__single' prefix '++' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} <> 'long' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'unsigned long' 1 +void test_cb_unary(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + // default-note@-1{{__counted_by attribute is here}} + // default-error@+1{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + ++p; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} test_cb_binary 'void (int *__single __counted_by(3))' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used p 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && 3 <= __builtin_get_pointer_upper_bound(p + 1) - p + 1 && 0 <= 3' +// CHECK-NEXT: | | |-CompoundAssignOperator {{.+}} 'int *__single __counted_by(3)':'int *__single' '+=' ComputeLHSTy='int *__single __counted_by(3)':'int *__single' ComputeResultTy='int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} <> 'long' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 1 +void test_cb_binary(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + // default-note@-1{{__counted_by attribute is here}} + // default-error@+1{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + p += 1; // expected-warning{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + diff --git a/clang/test/BoundsSafety/AST/counted_by_incdec.c b/clang/test/BoundsSafety/AST/counted_by_incdec.c new file mode 100644 index 0000000000000..878b4f4b3e3de --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_incdec.c @@ -0,0 +1,185 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct T { + int *__counted_by(len) ptr; + int len; +}; + +void Test(struct T *t) { + t->ptr++; + t->len--; +} + +// CHECK-LABEL: Test 'void (struct T *__single)' +// CHECK: | |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct T *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +void TestReverse(struct T *t) { + t->len--; + t->ptr++; +} + +// CHECK-LABEL: TestReverse 'void (struct T *__single)' +// CHECK: |-ParmVarDecl [[var_t_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t_1]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-MemberExpr {{.+}} ->len +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t_1]] +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_10]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 1 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/counted_by_or_null_call.c b/clang/test/BoundsSafety/AST/counted_by_or_null_call.c new file mode 100644 index 0000000000000..9c06fd32203a8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_or_null_call.c @@ -0,0 +1,610 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list %s | FileCheck %s + +#include + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +void foo(int *__counted_by_or_null(len) p, int len) {} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_1:0x[^ ]+]] {{.+}} caller_1 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 2 <= 0 - 0 && 0 <= 2' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +void caller_1() { + foo(0, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_2:0x[^ ]+]] {{.+}} caller_2 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 0 <= 0 - 0 && 0 <= 0' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +void caller_2() { + foo(0, 0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_3:0x[^ ]+]] {{.+}} caller_3 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_4:0x[^ ]+]] {{.+}} caller_4 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || -1 <= __builtin_get_pointer_upper_bound(&i) - &i && 0 <= -1' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_5:0x[^ ]+]] {{.+}} caller_5 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i_1:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || 2 <= __builtin_get_pointer_upper_bound(&i) - &i && 0 <= 2' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_6:0x[^ ]+]] {{.+}} caller_6 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_7:0x[^ ]+]] {{.+}} caller_7 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_8:0x[^ ]+]] {{.+}} caller_8 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +void caller_8(int *__single p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: {{^}}| |-ParmVarDecl [[var_out:0x[^ ]+]] +// CHECK: {{^}}| `-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: {{^}}| `-DependerDeclsAttr +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK: {{^}}|-FunctionDecl [[func_caller_9:0x[^ ]+]] {{.+}} caller_9 +// CHECK: {{^}}| |-ParmVarDecl [[var_out_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-CallExpr +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len)*__single, int *__single)' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_out_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single' +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK: {{^}}`-FunctionDecl [[func_caller_10:0x[^ ]+]] {{.+}} caller_10 +// CHECK: {{^}} |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_5:0x[^ ]+]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len)*__single, int *__single)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_22]] +// CHECK: {{^}} | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_23]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__single __counted_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' '=' +// CHECK: {{^}} | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}} | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | | |-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_29]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} diff --git a/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c b/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c new file mode 100644 index 0000000000000..c5b0c10879a5f --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c @@ -0,0 +1,265 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +void *__sized_by(siz) my_alloc(int siz); +int *__sized_by(cnt) my_alloc_int(int cnt); + +// CHECK: |-FunctionDecl [[func_my_alloc:0x[^ ]+]] {{.+}} my_alloc +// CHECK: |-FunctionDecl [[func_my_alloc_int:0x[^ ]+]] {{.+}} my_alloc_int + +void Foo(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc_int(siz); + p.len = 10; +} +// CHECK-LABEL: |-FunctionDecl {{.+}} Foo +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_siz_1:0x[^ ]+]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}}| | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_siz_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | | `-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(cnt)(*__single)(int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_my_alloc_int]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' + + +void FooBitCast(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: |-FunctionDecl {{.+}} FooBitCast +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_siz_2:0x[^ ]+]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}}| | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_siz_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | `-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(siz)(*__single)(int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_my_alloc]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void FooCCast(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = (int*)my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: `-FunctionDecl {{.+}} FooCCast +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_siz_3:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}} | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_siz_3]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(siz)(*__single)(int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_alloc]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | |-MemberExpr {{.+}} .len +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/dbpt-nullable.c b/clang/test/BoundsSafety/AST/dbpt-nullable.c new file mode 100644 index 0000000000000..771d9db2f2fa9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dbpt-nullable.c @@ -0,0 +1,83 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s --check-prefix=CHECK + +#include + +void counted(int *_Nullable __counted_by(count) array, int count) { + array[10]; +} + +// CHECK: |-FunctionDecl [[func_counted:0x[^ ]+]] {{.+}} counted +// CHECK: | |-ParmVarDecl [[var_array:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 10 + +void sized(int *_Nullable __sized_by(count) array, int count) { + array[10]; +} + +// CHECK: |-FunctionDecl [[func_sized:0x[^ ]+]] {{.+}} sized +// CHECK: | |-ParmVarDecl [[var_array_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 10 + +void ended(int *_Nullable __ended_by(end) array, int *end) { + array[10]; +} + +// CHECK: `-FunctionDecl [[func_ended:0x[^ ]+]] {{.+}} ended +// CHECK: |-ParmVarDecl [[var_array_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_array_2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(array) */ ':'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) _Nullable':'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_array_2]] +// CHECK: `-IntegerLiteral {{.+}} 10 diff --git a/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c b/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c new file mode 100644 index 0000000000000..d86789ef94930 --- /dev/null +++ b/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c @@ -0,0 +1,26 @@ + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -isystem %S/SystemHeaders/include %s | FileCheck %s +// RUN: %clang_cc1 -E -verify -fbounds-safety -isystem %S/SystemHeaders/include -o %t.pp.c %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety %t.pp.c | FileCheck %s + +// expected-no-diagnostics + +#include +#include + +int *__single return_to_single_p(int *p) { + return p; +} + +// CHECK-LABEL: increment_unsafe_p 'void (int *)' inline +// CHECK-NEXT: | |-ParmVarDecl {{.*}} used p 'int *' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *' postfix '++' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *' lvalue ParmVar {{.*}} 'p' 'int *' + +// CHECK-LABEL: return_to_single_p 'int *__single(int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used p 'int *__single' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: `-ReturnStmt +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__single' lvalue ParmVar {{.*}} 'p' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c b/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c new file mode 100644 index 0000000000000..06509ba6c9c19 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#define DISPATCH_GLOBAL_OBJECT(type, object) ((type)&(object)) +#define DISPATCH_DECL(name) typedef struct name##_s *name##_t +DISPATCH_DECL(dispatch_data); + +#define dispatch_data_empty \ + DISPATCH_GLOBAL_OBJECT(dispatch_data_t, _dispatch_data_empty) +extern struct dispatch_data_s _dispatch_data_empty; + +void foo(dispatch_data_t _Nonnull arg); + +// CHECK-LABEL: test 'void ()' +void test() { + foo(dispatch_data_empty); +} +// CHECK: `-CompoundStmt +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*__single)(struct dispatch_data_s *__single _Nonnull)' +// CHECK: | `-DeclRefExpr {{.*}} 'void (struct dispatch_data_s *__single _Nonnull)' Function {{.*}} 'foo' 'void (struct dispatch_data_s *__single _Nonnull)' +// CHECK: `-ParenExpr {{.*}} 'struct dispatch_data_s *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'struct dispatch_data_s *__single' +// CHECK: `-UnaryOperator {{.*}} 'struct dispatch_data_s *__single' prefix '&' cannot overflow +// CHECK: `-ParenExpr {{.*}} 'struct dispatch_data_s' lvalue +// CHECK: `-DeclRefExpr {{.*}} 'struct dispatch_data_s' lvalue Var {{.*}} '_dispatch_data_empty' 'struct dispatch_data_s' diff --git a/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c b/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c new file mode 100644 index 0000000000000..6f4f94a59e791 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c @@ -0,0 +1,825 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +// CHECK-LABEL: preincdec_init +void preincdec_init(char *__sized_by(len) p, unsigned long long len) { + char *lp = (--len, ++p); +} +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_lp:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} prefix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-AssumptionExpr +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} prefix '++' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: postincdec +void postincdec(char *__sized_by(len) p, unsigned long long len) { + p++, len--; +} +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: postincdec_init +void postincdec_init(char *__sized_by(len) p, unsigned long long len) { + char *lp = (len--, p++); +} +// CHECK: | |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_lp:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-AssumptionExpr +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: compound_assign_init +void compound_assign_init(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l = (p+=1, len-=1); +} +// CHECK: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && len - 1 <= (char *)__builtin_get_pointer_upper_bound(p + 1) - (char *__bidi_indexable)p + 1' +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='char *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} long' ComputeResultTy='unsigned +// CHECK: | | |-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_17]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: compound_assign_assign +void compound_assign_assign(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l; + l = (p+=1, len-=1); +} +// CHECK: | |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_l_1:0x[^ ]+]] +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_l_1]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && len - 1 <= (char *)__builtin_get_pointer_upper_bound(p + 1) - (char *__bidi_indexable)p + 1' +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='char *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_21]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_20]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: | | |-OpaqueValueExpr [[ove_22]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_25]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} long' ComputeResultTy='unsigned +// CHECK: | | |-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_init_zero +void assign_init_zero(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l = (p=0, len=0); +} +// CHECK: | |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l_2:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | `-OpaqueValueExpr [[ove_29]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_init +void assign_init(char *__sized_by(len) p, unsigned long long len, + char *__bidi_indexable ip, unsigned long long ilen) { + unsigned long long l = (p=ip, len=ilen); +} +// CHECK: | |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_ip:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_ilen:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l_3:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'ip <= __builtin_get_pointer_upper_bound(ip) && __builtin_get_pointer_lower_bound(ip) <= ip && ilen <= (char *)__builtin_get_pointer_upper_bound(ip) - (char *__bidi_indexable)ip' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_30]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_ip]] +// CHECK: | | `-OpaqueValueExpr [[ove_31]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_ilen]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK: | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_31]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_assign +void assign_assign(char *__sized_by(len) p, unsigned long long len, + char *__bidi_indexable ip, unsigned long long ilen) { + unsigned long long llen = 0; + char *__sized_by(llen) lp = 0; + lp = (len = ilen, p=ip); + llen = 0; +} +// CHECK: | |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_ip_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_ilen_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_llen:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_lp_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr {{.+}} 'llen == 0' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(llen)':'char *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_llen]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} '(len = ilen , p = ip) <= __builtin_get_pointer_upper_bound((len = ilen , p = ip)) && __builtin_get_pointer_lower_bound((len = ilen , p = ip)) <= (len = ilen , p = ip) && 0 <= (char *)__builtin_get_pointer_upper_bound((len = ilen , p = ip)) - (char *__single)(len = ilen , p = ip)' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(llen)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_lp_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_32]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsCheckExpr {{.+}} 'ip <= __builtin_get_pointer_upper_bound(ip) && __builtin_get_pointer_lower_bound(ip) <= ip && ilen <= (char *)__builtin_get_pointer_upper_bound(ip) - (char *__bidi_indexable)ip' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | | | |-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_33]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ilen_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_34]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ip_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_llen]] +// CHECK: | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: for_cond_inc +char for_cond_inc(char *__sized_by(len) p, unsigned long long len) { + char val = *p; + for (p++, len--; len > 0; p++, len--) { + val += *p; + } + return val; +} +// CHECK: |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_val:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_36]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | `-OpaqueValueExpr [[ove_37]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | |-OpaqueValueExpr [[ove_36]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | `-OpaqueValueExpr [[ove_37]] {{.*}} 'unsigned long long' +// CHECK: |-ForStmt +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_40:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | | |-OpaqueValueExpr [[ove_41:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_42:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_43:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_38]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | |-OpaqueValueExpr [[ove_39]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_40]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_41]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_43]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_42]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_38]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_42]] {{.*}} 'unsigned long long' +// CHECK: | |-BinaryOperator {{.+}} 'int' '>' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_44:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_45:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_46:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | | |-OpaqueValueExpr [[ove_47:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_48:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_49:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_44]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | |-OpaqueValueExpr [[ove_45]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_46]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_44]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_47]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_49]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_48]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_49]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_49]] {{.*}} lvalue +// CHECK: | `-CompoundStmt + diff --git a/clang/test/BoundsSafety/AST/dynamic-inout-count.c b/clang/test/BoundsSafety/AST/dynamic-inout-count.c new file mode 100644 index 0000000000000..27a4edf52382e --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-inout-count.c @@ -0,0 +1,134 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-IntegerLiteral {{.+}} 42 +void foo(int *__counted_by(*len) buf, int *len) { + *len = 42; +} + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-IntegerLiteral {{.+}} 11 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len), int *__single)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'long' +void bar() { + int arr[10]; + int len = 11; + foo(arr, &len); +} diff --git a/clang/test/BoundsSafety/AST/dynamic-range-init-list.c b/clang/test/BoundsSafety/AST/dynamic-range-init-list.c new file mode 100644 index 0000000000000..2b4bd2aa544b7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-range-init-list.c @@ -0,0 +1,85 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple x86_64 %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple x86_64 %s | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +void Test(void) { + int arr[10]; + struct RangePtrs rptrs = { arr + 1, arr + 2, arr + 3 }; +} + +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: {{^}} `-DeclStmt +// CHECK: {{^}} `-VarDecl [[var_rptrs:0x[^ ]+]] +// CHECK: {{^}} `-BoundsCheckExpr {{.+}} 'arr + 2 <= __builtin_get_pointer_upper_bound(arr + 1) && arr + 1 <= arr + 2 && __builtin_get_pointer_lower_bound(arr + 1) <= arr + 1' +// CHECK: {{^}} |-BoundsCheckExpr {{.+}} 'arr + 3 <= __builtin_get_pointer_upper_bound(arr + 2) && arr + 2 <= arr + 3 && __builtin_get_pointer_lower_bound(arr + 2) <= arr + 2' +// CHECK: {{^}} | |-InitListExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__single /* __started_by(iter) */ ':'void *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}} `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 3 + diff --git a/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c b/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c new file mode 100644 index 0000000000000..0745c5054a372 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK-NOT: BoundsCheckExpr +// CHECK-NOT: BoundsSafetyPointerPromotionExpr +// CHECK-NOT: MaterializeSequenceExpr + +// CHECK: FunctionDecl {{.+}} foo 'void (int * __ended_by(end), int * /* __started_by(p) */ )' +void foo(int *__ended_by(end) p, int *end) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + (void)*p; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)p[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __ended_by(end)':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __ended_by(end)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int * /* __started_by(p) */ ':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' lvalue ParmVar {{.+}} 'end' 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * /* __started_by(p) */ ':'int *' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' lvalue ParmVar {{.+}} 'end' 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + p = p + 42; + end = end - 42; +} + +// CHECK: RecordDecl {{.+}} struct bar definition +// CHECK: |-FieldDecl {{.+}} q 'int * __ended_by(end)':'int *' +// CHECK-NEXT: `-FieldDecl {{.+}} end 'int * /* __started_by(q) */ ':'int *' +struct bar { + int *__ended_by(end) q; + int *end; +}; + +// CHECK: FunctionDecl {{.+}} baz 'void (struct bar *)' +void baz(struct bar *b) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + (void)*b->q; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)b->q[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __ended_by(end)':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __ended_by(end)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int * /* __started_by(q) */ ':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' lvalue ->end + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * /* __started_by(q) */ ':'int *' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' lvalue ->end + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + b->q = b->q + 42; + b->end = b->end - 42; +} diff --git a/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c b/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c new file mode 100644 index 0000000000000..86e771d5d98ec --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c @@ -0,0 +1,187 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fbounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: | |-ParmVarDecl [[var_start:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'end - 1UL <= __builtin_get_pointer_upper_bound(start + 1UL) && start + 1UL <= end - 1UL && __builtin_get_pointer_lower_bound(start + 1UL) <= start + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_start]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_start]] +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// WITHOUT: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// WITHOUT: | |-ParmVarDecl [[var_start:0x[^ ]+]] +// WITHOUT: | |-ParmVarDecl [[var_end:0x[^ ]+]] +// WITHOUT: | `-CompoundStmt +// WITHOUT: | |-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | | `-UnaryOperator {{.+}} postfix '--' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_end]] +// WITHOUT: | | `-IntegerLiteral {{.+}} 0 +// WITHOUT: | `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | `-UnaryOperator {{.+}} postfix '++' +// WITHOUT: | | `-DeclRefExpr {{.+}} [[var_start]] +// WITHOUT: | `-IntegerLiteral {{.+}} 0 + +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK: `-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-ParmVarDecl [[var_start_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_end_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ParenExpr +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'end - 1 <= __builtin_get_pointer_upper_bound(start + 1) && start + 1 <= end - 1 && __builtin_get_pointer_lower_bound(start + 1) <= start + 1' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ParenExpr +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// WITHOUT: `-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// WITHOUT: |-ParmVarDecl [[var_start_1:0x[^ ]+]] +// WITHOUT: |-ParmVarDecl [[var_end_1:0x[^ ]+]] +// WITHOUT: `-CompoundStmt +// WITHOUT: |-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | `-ParenExpr +// WITHOUT: | | `-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// WITHOUT: | | |-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// WITHOUT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT: | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | `-IntegerLiteral {{.+}} 1 +// WITHOUT: | `-IntegerLiteral {{.+}} 0 +// WITHOUT: `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | `-ParenExpr +// WITHOUT: | `-BinaryOperator {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' '=' +// WITHOUT: | |-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// WITHOUT: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT: | | |-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | `-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | `-IntegerLiteral {{.+}} 1 +// WITHOUT: `-IntegerLiteral {{.+}} 0 + +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} diff --git a/clang/test/BoundsSafety/AST/ended_by_assign_checks.c b/clang/test/BoundsSafety/AST/ended_by_assign_checks.c new file mode 100644 index 0000000000000..0c27ae524ee18 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_assign_checks.c @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2> /dev/null | FileCheck %s + +#include + +struct DataWithEndedBy { + int *__ended_by(fend) fbegin; + int *fend; +}; + +// CHECK-LABEL: test +void test(struct DataWithEndedBy *data, int len) { + int arr[10]; + data->fbegin = arr; + data->fend = arr + 10; +} +// CHECK: | |-ParmVarDecl [[var_data:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | | |-MemberExpr {{.+}} ->fend +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: test_bitcast +void test_bitcast(struct DataWithEndedBy *data, int len) { + char arr[10]; + data->fbegin = arr; + data->fend = arr + 10; +} +// CHECK: | |-ParmVarDecl [[var_data_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | | |-MemberExpr {{.+}} ->fend +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: test_ext_bitcast +void test_ext_bitcast(struct DataWithEndedBy *data, int len) { + char arr[10]; + data->fbegin = (int *)arr; + data->fend = (int *)(arr + 10); +} +// CHECK: |-ParmVarDecl [[var_data_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/ended_by_decls.c b/clang/test/BoundsSafety/AST/ended_by_decls.c new file mode 100644 index 0000000000000..30a229b0f86df --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_decls.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x c++ %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x objective-c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x objective-c++ %s 2>&1 | FileCheck %s + +#include + +// CHECK: RecordDecl {{.*}} struct S definition +// CHECK: |-FieldDecl {{.*}} referenced end 'int *{{.*}}/* __started_by(iter) */ ' +// CHECK: |-FieldDecl {{.*}} referenced start 'int *{{.*}}__ended_by(iter)' +// CHECK: `-FieldDecl {{.*}} referenced iter 'int *{{.*}}__ended_by(end) /* __started_by(start) */ ' +struct S { + int *end; + int *__ended_by(iter) start; + int *__ended_by(end) iter; +}; + +// CHECK: FunctionDecl {{.*}} foo 'void (int *{{.*}}__ended_by(end), int *{{.*}}/* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +// CHECK: `-ParmVarDecl {{.*}} used end 'int *{{.*}}/* __started_by(start) */ ' +void foo(int *__ended_by(end) start, int* end); + +// CHECK: FunctionDecl {{.*}} foo_cptr_end 'void (int *{{.*}}__ended_by(end), char *{{.*}}/* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +// CHECK: `-ParmVarDecl {{.*}} used end 'char *{{.*}}/* __started_by(start) */ ' +void foo_cptr_end(int *__ended_by(end) start, char* end); + +// CHECK: FunctionDecl {{.*}} foo_seq 'void (int *{{.*}}__ended_by(next), int *{{.*}}__ended_by(end) /* __started_by(start) */ , char *{{.*}}/* __started_by(next) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(next)' +// CHECK: |-ParmVarDecl {{.*}} used next 'int *{{.*}}__ended_by(end) /* __started_by(start) */ ' +// CHECK: `-ParmVarDecl {{.*}} used end 'char *{{.*}}/* __started_by(next) */ ' +void foo_seq(int *__ended_by(next) start, int *__ended_by(end) next, char* end); + +// CHECK: FunctionDecl {{.*}} foo_out_start_out_end 'void (int *{{.*}}__ended_by(*out_end)*{{.*}}, int *{{.*}}/* __started_by(*out_start) */ *{{.*}})' +// CHECK: |-ParmVarDecl {{.*}} used out_start 'int *{{.*}}__ended_by(*out_end)*{{.*}}' +// CHECK: `-ParmVarDecl {{.*}} used out_end 'int *{{.*}}/* __started_by(*out_start) */ *{{.*}}' +void foo_out_start_out_end(int *__ended_by(*out_end) *out_start, int **out_end); + +// CHECK: FunctionDecl {{.*}} foo_out_end 'void (int *{{.*}}__ended_by(*out_end), int *{{.*}}/* __started_by(start) */ *{{.*}})' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(*out_end)':'int *{{.*}}' +// CHECK: `-ParmVarDecl {{.*}} used out_end 'int *{{.*}}/* __started_by(start) */ *{{.*}}' +void foo_out_end(int *__ended_by(*out_end) start, int **out_end); + +// CHECK: FunctionDecl {{.*}} foo_ret_end 'int *{{.*}}__ended_by(end)(int *{{.*}})' +// CHECK: `-ParmVarDecl {{.*}} used end 'int *{{.*}}' +int *__ended_by(end) foo_ret_end(int *end); + +// CHECK: FunctionDecl {{.*}} foo_local_ended_by 'void ({{.*}})' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used end 'int *{{.*}}/* __started_by(start) */ ' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +void foo_local_ended_by(void) { + int *end; + int *__ended_by(end) start; +} diff --git a/clang/test/BoundsSafety/AST/ended_by_incdec.c b/clang/test/BoundsSafety/AST/ended_by_incdec.c new file mode 100644 index 0000000000000..28ea0aaa9039f --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_incdec.c @@ -0,0 +1,150 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct T { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void Test(struct T *t) { + t->start++; + --t->iter; + t->end--; +} + +// CHECK-LABEL: Test 'void (struct T *__single)' +// CHECK: |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 't->end - 1UL <= __builtin_get_pointer_upper_bound(t->iter - 1UL) && t->iter - 1UL <= t->end - 1UL && __builtin_get_pointer_lower_bound(t->iter - 1UL) <= t->iter - 1UL' +// CHECK: | | |-BoundsCheckExpr {{.+}} 't->iter - 1UL <= __builtin_get_pointer_upper_bound(t->start + 1UL) && t->start + 1UL <= t->iter - 1UL && __builtin_get_pointer_lower_bound(t->start + 1UL) <= t->start + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MemberExpr {{.+}} ->start +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->iter +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-MemberExpr {{.+}} ->iter +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->end +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_9]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->iter +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 1 +// CHECK: |-UnaryOperator {{.+}} prefix '--' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-UnaryOperator {{.+}} postfix '--' +// CHECK: | `-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' + diff --git a/clang/test/BoundsSafety/AST/ended_by_locals.c b/clang/test/BoundsSafety/AST/ended_by_locals.c new file mode 100644 index 0000000000000..7e9f5b28d5354 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_locals.c @@ -0,0 +1,75 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +void foo(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +// CHECK:FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK:|-ParmVarDecl [[var_asdf:0x[^ ]+]] +// CHECK:|-ParmVarDecl [[var_asdf_len:0x[^ ]+]] +// CHECK:`-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_myEndPtr:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__single /* __started_by(myEndedByPtr) */ ':'const int *__single' +// CHECK: | `-BoundsCheckExpr {{.+}} 'asdf + asdf_len <= __builtin_get_pointer_upper_bound(asdf + asdf_len) && __builtin_get_pointer_lower_bound(asdf + asdf_len) <= asdf + asdf_len' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_asdf_len]] +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_asdf_len]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_myEndedByPtr:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'myEndPtr <= __builtin_get_pointer_upper_bound(asdf) && asdf <= myEndPtr && __builtin_get_pointer_lower_bound(asdf) <= asdf' +// CHECK: |-ImplicitCastExpr {{.+}} 'const int *__single __ended_by(myEndPtr)':'const int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'const int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const int *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const int *' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-GetBoundExpr {{.+}} lower +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: `-BoundsSafetyPointerPromotionExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: |-DeclRefExpr {{.+}} [[var_myEndPtr]] +// CHECK: |-ImplicitCastExpr {{.+}} 'const int *__single /* __started_by(myEndedByPtr) */ ':'const int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_myEndPtr]] +// CHECK: `-ImplicitCastExpr {{.+}} 'const int *__single __ended_by(myEndPtr)':'const int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_myEndedByPtr]] + diff --git a/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c b/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c new file mode 100644 index 0000000000000..dbbe86115802a --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c @@ -0,0 +1,863 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -verify %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -verify -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:12:1, col:{{.+}}> col:{{.+}} used ended_by 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_in_bounds 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local) && local <= &local[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_start_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local - 2) && local - 2 <= &local[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_end_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'local + 11 <= __builtin_get_pointer_upper_bound(local) && local <= local + 11' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 11 +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explicit_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used ilocal 'char *__indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&ilocal[10] <= __builtin_get_pointer_upper_bound(ilocal) && ilocal <= &ilocal[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explict_bidi_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used bilocal 'char *__bidi_indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&bilocal[10] <= __builtin_get_pointer_upper_bound(bilocal) && bilocal <= &bilocal[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_ended_by 'void (char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'end <= __builtin_get_pointer_upper_bound(start) && start <= end' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} pass_counted_by 'void (char *__single __counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used start 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'void' 'start + count <= __builtin_get_pointer_upper_bound(start) && start <= start + count' +// CHECK-NEXT: | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/AST/ended_by_param_in_call.c b/clang/test/BoundsSafety/AST/ended_by_param_in_call.c new file mode 100644 index 0000000000000..1e463b55944de --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_param_in_call.c @@ -0,0 +1,1017 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -verify %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -verify -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:12:1, col:{{.+}}> col:{{.+}} used ended_by 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_in_bounds 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local) && local <= &local[10] && __builtin_get_pointer_lower_bound(local) <= local' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_start_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local - 2) && local - 2 <= &local[10] && __builtin_get_pointer_lower_bound(local - 2) <= local - 2' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_end_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'local + 11 <= __builtin_get_pointer_upper_bound(local) && local <= local + 11 && __builtin_get_pointer_lower_bound(local) <= local' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 11 +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explicit_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used ilocal 'char *__indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&ilocal[10] <= __builtin_get_pointer_upper_bound(ilocal) && ilocal <= &ilocal[10] && __builtin_get_pointer_lower_bound(ilocal) <= ilocal' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explict_bidi_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used bilocal 'char *__bidi_indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&bilocal[10] <= __builtin_get_pointer_upper_bound(bilocal) && bilocal <= &bilocal[10] && __builtin_get_pointer_lower_bound(bilocal) <= bilocal' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_ended_by 'void (char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'end <= __builtin_get_pointer_upper_bound(start) && start <= end && __builtin_get_pointer_lower_bound(start) <= start' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} pass_counted_by 'void (char *__single __counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used start 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'void' 'start + count <= __builtin_get_pointer_upper_bound(start) && start <= start + count && __builtin_get_pointer_lower_bound(start) <= start' +// CHECK-NEXT: | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/AST/ended_by_returns.c b/clang/test/BoundsSafety/AST/ended_by_returns.c new file mode 100644 index 0000000000000..2665227c8b9ce --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_returns.c @@ -0,0 +1,492 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s + +#include + +char *__ended_by(end) chunk(char *__ended_by(end) begin, char *end) { + return begin + 1; +} +//CHECK: FunctionDecl [[func_chunk:0x[^ ]+]] {{.*}} used chunk 'char *__single __ended_by(end)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +//CHECK: |-ParmVarDecl [[var_begin:0x[^ ]+]] {{.*}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] {{.*}} 'char *__single /* __started_by(begin) */ ':'char *__single' +//CHECK: `-CompoundStmt +//CHECK: `-ReturnStmt +//CHECK: `-ImplicitCastExpr {{.*}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: `-BinaryOperator {{.*}} 'char *__bidi_indexable' '+' +//CHECK: |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +//CHECK: | |-DeclRefExpr {{.+}} [[var_begin]] +//CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +//CHECK: | | `-DeclRefExpr {{.+}} [[var_end]] +//CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: | `-DeclRefExpr {{.+}} [[var_begin]] +//CHECK: `-IntegerLiteral {{.*}} 'int' 1 + +// CHECK-LABEL: foo +void foo(void) { + int arr[10]; + int *p = chunk(arr, arr+10); +} +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: fooCast +void fooCast(void) { + int arr[10]; + int *p = (int*)chunk(arr, arr+10); +} +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' + +struct DataWithEndedBy { + int *__ended_by(fend) fbegin; + int *fend; +}; + +// CHECK-LABEL: bar +void bar(struct DataWithEndedBy *data) { + int arr[10]; + data->fbegin = chunk(arr, arr+10); + data->fend = arr+10; +} +// CHECK: |-ParmVarDecl [[var_data:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: barCast +void barCast(struct DataWithEndedBy *data) { + int arr[10]; + data->fbegin = (int*)chunk(arr, arr+10); + data->fend = arr+10; +} +// CHECK: |-ParmVarDecl [[var_data_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' + +struct DataWithCountedBy { + long *__counted_by(count) ptr; + unsigned long long count; +}; + +// CHECK-LABEL: baz 'void (struct DataWithCountedBy *__single, int)' +void baz(struct DataWithCountedBy *data, int len) { + int arr[10]; + data->ptr = chunk(arr, arr+10); + data->count = len; +} +// CHECK: |-ParmVarDecl [[var_data_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_4:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'long *__single __counted_by(count)':'long *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *__single __counted_by(count)':'long *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_4]] +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_4]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_15]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_15]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: bazCast 'void (struct DataWithCountedBy *__single, int)' +void bazCast(struct DataWithCountedBy *data, int len) { + int arr[10]; + data->ptr = (long*)chunk(arr, arr+10); + data->count = len; +} +// CHECK: |-ParmVarDecl [[var_data_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_5:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'long *__single __counted_by(count)':'long *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *__single __counted_by(count)':'long *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_5]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_19]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_3]] +// CHECK: | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' diff --git a/clang/test/BoundsSafety/AST/ended_by_type.c b/clang/test/BoundsSafety/AST/ended_by_type.c new file mode 100644 index 0000000000000..d5d9647bca947 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_type.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-objc-experimental -x objective-c -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -x c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s + +#include + +// BS: VarDecl {{.+}} func_ptr_dd 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// BSA: VarDecl {{.+}} func_ptr_dd 'void (*)(void * __ended_by(end), void * /* __started_by(start) */ )' +void (*func_ptr_dd)(void *__ended_by(end) start, void *end); + +// BS: VarDecl {{.+}} func_ptr_di 'void (*__single)(void *__single __ended_by(*end), void *__single /* __started_by(start) */ *__single)' +// BSA: VarDecl {{.+}} func_ptr_di 'void (*)(void * __ended_by(*end), void * /* __started_by(start) */ *)' +void (*func_ptr_di)(void *__ended_by(*end) start, void **end); + +// BS: VarDecl {{.+}} func_ptr_id 'void (*__single)(void *__single __ended_by(end)*__single, void *__single /* __started_by(*start) */ )' +// BSA: VarDecl {{.+}} func_ptr_id 'void (*)(void * __ended_by(end)*, void * /* __started_by(*start) */ )' +void (*func_ptr_id)(void *__ended_by(end) *start, void *end); + +// BS: VarDecl {{.+}} func_ptr_ii 'void (*__single)(void *__single __ended_by(*end)*__single, void *__single /* __started_by(*start) */ *__single)' +// BSA: VarDecl {{.+}} func_ptr_ii 'void (*)(void * __ended_by(*end)*, void * /* __started_by(*start) */ *)' +void (*func_ptr_ii)(void *__ended_by(*end) *start, void **end); + +void foo(void) { + // BS: CStyleCastExpr {{.+}} 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' + // BSA: CStyleCastExpr {{.+}} 'void (*)(void * __ended_by(end), void * /* __started_by(start) */ )' + (void (*)(void *__ended_by(end) start, void *end))0; +} diff --git a/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c b/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c new file mode 100644 index 0000000000000..7c642cd72a982 --- /dev/null +++ b/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +int main() { + int *ip = 0; + char *cp = (char*)ip; + + return 0; +} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} cp 'char *__bidi_indexable'{{.*}} cinit +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'ip' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c b/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c new file mode 100644 index 0000000000000..b2f1aaa3922c3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c @@ -0,0 +1,61 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[]; +} flex_t; + +// CHECK-LABEL: init_null 'void (void)' +void init_null(void) { + flex_t *__single s = 0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.*}} s 'flex_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: init_casted_null 'void (void)' +void init_casted_null(void) { + flex_t *__single s = (flex_t *)0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.*}} s 'flex_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.*}} 'flex_t *' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: assign_null 'void (void)' +void assign_null(void) { + flex_t *__single s; + s = 0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.*}} used s 'flex_t *__single' +// CHECK-NEXT: `-BinaryOperator {{.*}} 'flex_t *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'flex_t *__single' lvalue Var {{.*}} 's' 'flex_t *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: assign_casted_null 'void (void)' +void assign_casted_null(void) { + flex_t *__single s; + s = (flex_t *)0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.*}} used s 'flex_t *__single' +// CHECK-NEXT: `-BinaryOperator {{.*}} 'flex_t *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'flex_t *__single' lvalue Var {{.*}} 's' 'flex_t *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.*}} 'flex_t *' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..0b0670f0bc8f8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c @@ -0,0 +1,64 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: init_single +void init_single(void *p) { + struct flexible *__single s = p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p]] + +// CHECK-LABEL: init_casted_single +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p_1]] + +// CHECK-LABEL: assign_single +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | |-DeclStmt +// CHECK-NEXT: | | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p_2]] + +// CHECK-LABEL: assign_casted_single +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} +// CHECK-NEXT: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s_3:0x[^ ]+]] +// CHECK-NEXT: `-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.+}} [[var_s_3]] +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} [[var_p_3]] diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c new file mode 100644 index 0000000000000..87c6f73cab8c2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c @@ -0,0 +1,125 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[]; +} flex_inner_t; + +typedef struct { + unsigned dummy; + flex_inner_t flex; +} flex_t; + + +// CHECK-LABEL: test_fam_base +void test_fam_base(flex_t *f, void *__bidi_indexable buf) { + f = buf; +} + +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: |-DeclRefExpr {{.+}} [[var_f]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf]] + + +// CHECK-LABEL: test_fam_base_with_count +void test_fam_base_with_count(flex_t *f, void *__bidi_indexable buf) { + f = buf; + f->flex.count = 10; +} + +// CHECK: |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: `-IntegerLiteral {{.+}} 10 + +// CHECK-LABEL: test_fam_base_init +void test_fam_base_init(void *__bidi_indexable buf) { + flex_t *__single f = buf; +} +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f_2:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + + +// CHECK-LABEL: test_fam_base_init_with_count +void test_fam_base_init_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + f->flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_3:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_f_3]] +// CHECK: `-IntegerLiteral {{.+}} 10 + +// CHECK: VarDecl [[var_g_flex:0x[^ ]+]] +flex_inner_t g_flex; + +// CHECK-LABEL: test_fam_lvalue_base_count_assign +void test_fam_lvalue_base_count_assign(unsigned arg) { + g_flex.count = arg; +} +// CHECK: |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: `-DeclRefExpr {{.+}} [[var_arg]] + +// CHECK-LABEL: test_fam_lvalue_base_count_decrement +void test_fam_lvalue_base_count_decrement() { + g_flex.count--; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-UnaryOperator {{.+}} postfix '--' +// CHECK: | `-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] + +// CHECK-LABEL: test_fam_lvalue_base_count_compound +void test_fam_lvalue_base_count_compound(unsigned arg) { + g_flex.count -= arg; +} +// CHECK: |-ParmVarDecl [[var_arg_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-CompoundAssignOperator {{.+}} ComputeLHSTy='unsigned int' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: `-DeclRefExpr {{.+}} [[var_arg_1]] diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c b/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c new file mode 100644 index 0000000000000..13550a3f0cc64 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c @@ -0,0 +1,760 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_simple_no_flexbase_update:0x[^ ]+]] {{.+}} simple_no_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +// rdar://132731845 +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_simple_flexbase_update:0x[^ ]+]] {{.+}} simple_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +// rdar://132731845 +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_simple_flexbase_self_assign:0x[^ ]+]] {{.+}} simple_flexbase_self_assign +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Simple *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| `-FieldDecl + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz +// CHECK-NEXT: {{^}}| `-ParmVarDecl [[var_len:0x[^ ]+]] + +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_shared_no_flexbase_update:0x[^ ]+]] {{.+}} shared_no_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' + +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_no_flexbase_update_reverse:0x[^ ]+]] {{.+}} shared_no_flexbase_update_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'baz(11) <= __builtin_get_pointer_upper_bound(baz(11)) && __builtin_get_pointer_lower_bound(baz(11)) <= baz(11) && 11 <= __builtin_get_pointer_upper_bound(baz(11)) - baz(11) && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' + +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_update:0x[^ ]+]] {{.+}} shared_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p3 <= __builtin_get_pointer_upper_bound(p3) && __builtin_get_pointer_lower_bound(p3) <= p3 && 11 <= __builtin_get_pointer_upper_bound(p3) - p3 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p2_2]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p3]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_2]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' + +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_update_reverse:0x[^ ]+]] {{.+}} shared_flexbase_update_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p3 <= __builtin_get_pointer_upper_bound(p3) && __builtin_get_pointer_lower_bound(p3) <= p3 && 11 <= __builtin_get_pointer_upper_bound(p3) - p3 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p2_3]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p3_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_3]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' + +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign:0x[^ ]+]] {{.+}} shared_flexbase_self_assign +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_21]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_4]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' + +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_reverse:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_25]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2_5]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' + +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_fr:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_fr +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p->ptr <= __builtin_get_pointer_upper_bound(p->ptr) && __builtin_get_pointer_lower_bound(p->ptr) <= p->ptr && 11 <= __builtin_get_pointer_upper_bound(p->ptr) - p->ptr && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_29]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_31]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' + +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_fr_reverse:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_fr_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p->ptr <= __builtin_get_pointer_upper_bound(p->ptr) && __builtin_get_pointer_lower_bound(p->ptr) <= p->ptr && 11 <= __builtin_get_pointer_upper_bound(p->ptr) - p->ptr && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | | |-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_33]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_35]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_36]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_34]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_double_no_flexbase_update_once:0x[^ ]+]] {{.+}} double_no_flexbase_update_once +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_11]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_37]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} +// CHECK-NEXT: {{^}}`-FunctionDecl [[func_double_no_flexbase_update_both:0x[^ ]+]] {{.+}} double_no_flexbase_update_both +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_p_12:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_38]] +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | |-MemberExpr {{.+}} ->len2 +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_38]] {{.*}} 'int' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c new file mode 100644 index 0000000000000..563f5b461e2bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c @@ -0,0 +1,353 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_inner_t; + +typedef struct { + unsigned dummy; + flex_inner_t flex; +} flex_t; + + +// CHECK-LABEL: test_fam_base +void test_fam_base(flex_t *f, void *__bidi_indexable buf) { + f = buf; +} +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf]] + +// CHECK-LABEL: test_fam_base_with_count +void test_fam_base_with_count(flex_t *f, void *__bidi_indexable buf) { + f = buf; + f->flex.count = 10; +} + +// CHECK: |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' + + +// CHECK-LABEL: test_fam_base_init +void test_fam_base_init(void *__bidi_indexable buf) { + flex_t *__single f = buf; +} +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f_2:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + +// CHECK-LABEL: test_fam_base_init_with_count +void test_fam_base_init_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + f->flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_3:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_3]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + + +// FIXME: rdar://84810920 +// CHECK-LABEL: test_fam_base_init_deref_with_count +void test_fam_base_init_deref_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + (*f).flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_4:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .count +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '10 <= (*f).flex.count && 0 <= 10' +// FIXME: Ignoring the rest of AST for now. It seems file check doesn't like +// the dump produced by our ast simplfier for this function. rdar://103050286 + +// CHECK: VarDecl [[var_g_flex:0x[^ ]+]] +flex_inner_t g_flex = {4, {1, 2, 3, 4}}; + +// CHECK-LABEL: test_fam_lvalue_base_count_assign +void test_fam_lvalue_base_count_assign(unsigned arg) { + g_flex.count = arg; +} +// CHECK: | |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'arg <= g_flex.count && 0 <= arg' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | | |-MemberExpr {{.+}} .count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_18]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arg]] + +// CHECK-LABEL: test_fam_lvalue_base_count_decrement +void test_fam_lvalue_base_count_decrement() { + g_flex.count--; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_19]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_20]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_21]] +// CHECK: | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 1 + +// CHECK-LABEL: test_fam_lvalue_base_count_compound +void test_fam_lvalue_base_count_compound(unsigned arg) { + g_flex.count -= arg; +} +// CHECK: | |-ParmVarDecl [[var_arg_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeLHSTy='unsigned int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned int' +// CHECK: | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned int' +// CHECK: | | |-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_22]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_24]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arg_1]] +// CHECK: | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | |-OpaqueValueExpr [[ove_22]] +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_25]] +// CHECK: | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned int' + +typedef struct { + unsigned char count; + int elems[__counted_by(count - 1)]; +} flex_uchar_t; + +void test_flex_uchar_count_conversion(flex_uchar_t *flex, int arg) { + flex = flex; + flex->count = arg; +} +// CHECK: |-FunctionDecl [[func_test_flex_uchar_count_conversion:0x[^ ]+]] {{.+}} test_flex_uchar_count_conversion +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_arg_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BinaryOperator {{.+}} 'flex_uchar_t *__single' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'unsigned char' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_25]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arg_2]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned char' '=' +// CHECK: | | |-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned char' +// CHECK: | |-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned char' +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c new file mode 100644 index 0000000000000..aad6ba7553038 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c @@ -0,0 +1,113 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[]; +}; + +// CHECK-LABEL: not_checking_count_single +int not_checking_count_single(struct flexible *__single flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: not_checking_count_unsafe +int not_checking_count_unsafe(struct flexible *__unsafe_indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_indexable +int checking_count_indexable(struct flexible *__indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_bidi_indexable +int checking_count_bidi_indexable(struct flexible *__indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_sized_by +int checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { + return (*flex).elems[12]; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-MemberExpr {{.+}} .elems +// CHECK: | `-ParenExpr +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-IntegerLiteral {{.+}} 12 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c new file mode 100644 index 0000000000000..e6fbedbcebd01 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c @@ -0,0 +1,283 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: not_checking_count_single +int not_checking_count_single(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + + +// CHECK-LABEL: not_checking_count_unsafe +int not_checking_count_unsafe(struct flexible *__unsafe_indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_indexable +int checking_count_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_bidi_indexable +int checking_count_bidi_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_3:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + + +// CHECK-LABEL: checking_count_sized_by +int checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { +// CHECK: | |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c new file mode 100644 index 0000000000000..e860f4f772da2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c @@ -0,0 +1,338 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void sink(struct flexible *__single flex); +// CHECK: |-FunctionDecl [[func_sink:0x[^ ]+]] {{.+}} sink + +// CHECK-LABEL: checking_count_single +void checking_count_single(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] + sink(flex); +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' + +} + +// CHECK-LABEL: checking_count_indexable +void checking_count_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] + sink(flex); +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_2]] +} + +// CHECK-LABEL: checking_count_bidi_indexable +void checking_count_bidi_indexable(struct flexible *__indexable flex) { +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_3:0x[^ ]+]] + sink(flex); +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_3]] +} + +// CHECK-LABEL: checking_count_sized_by +void checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] + sink(flex); +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' + +} + +// FIXME: Unnecessary promotion +// CHECK-LABEL: checking_count_single_return +struct flexible *checking_count_single_return(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex_5:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_5]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' + return flex; +} + +// CHECK-LABEL: checking_count_indexable_return +struct flexible *checking_count_indexable_return(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_6:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_6]] +// CHECK: | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' + return flex; +} + +// CHECK-LABEL: checking_count_bidi_indexable_return +struct flexible *checking_count_bidi_indexable_return(struct flexible *__bidi_indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_7:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_7]] +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' + return flex; +} + +// CHECK-LABEL: checking_count_sized_by_return +struct flexible *checking_count_sized_by_return(struct flexible *__sized_by(size) flex, unsigned long long size) { +// CHECK: | |-ParmVarDecl [[var_flex_8:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_8]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' + + return flex; +} + +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c b/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c new file mode 100644 index 0000000000000..9586e18c4a997 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flex_uchar { + unsigned char len; + unsigned char data[__counted_by(len - 1)]; +}; + +struct flex_uchar init = { 3, {0, 1} }; +// CHECK: `-VarDecl {{.+}} init 'struct flex_uchar' cinit +// CHECK: `-InitListExpr {{.+}} 'struct flex_uchar' +// CHECK: |-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK: `-InitListExpr {{.+}} 'unsigned char[2]' +// CHECK: |-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: `-IntegerLiteral {{.+}} 'int' 1 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..b50e30b94d63f --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c @@ -0,0 +1,64 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +void f(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + s->offs = 5; + s->len = 10; +} +// CHECK: `-FunctionDecl [[func_f:0x[^ ]+]] {{.+}} f +// CHECK: |-ParmVarDecl [[var_s:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | `-InitListExpr +// CHECK: | |-array_filler: ImplicitValueInitExpr +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'S *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'S *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'S *__bidi_indexable' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ArraySubscriptExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->offs +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->len +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c new file mode 100644 index 0000000000000..acffec83e973e --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c @@ -0,0 +1,284 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + unsigned long long count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: promote_to_bidi_indexable +void promote_to_bidi_indexable(struct flexible *flex) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + + +// CHECK-LABEL: promote_null_to_bidi_indexable +void promote_null_to_bidi_indexable(void) { + struct flexible *b = (struct flexible *)0; +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_null_to_single +void promote_null_to_single() { + struct flexible *__single b = (struct flexible *)0; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_b_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_to_single +void promote_to_single(struct flexible *flex) { + struct flexible *__single s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: double_promote_to_bidi_indexable +void double_promote_to_bidi_indexable(struct flexible *__sized_by(size) flex, int size) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +// CHECK-LABEL: promote_to_sized_by +void promote_to_sized_by(struct flexible *flex) { + unsigned long long siz; + struct flexible *__sized_by(siz) s; + + siz = sizeof(struct flexible) + sizeof(int) * flex->count; + s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && sizeof(struct flexible) + sizeof(int) * flex->count <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *__bidi_indexable)flex' +// CHECK: | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '*' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + +// CHECK-LABEL: promote_to_single_assign +void promote_to_single_assign(struct flexible *flex) { + struct flexible *__single s = flex; + s->count = flex->count; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-MemberExpr {{.+}} ->count +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c new file mode 100644 index 0000000000000..8a4c6bdd1d43e --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c @@ -0,0 +1,251 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: promote_to_bidi_indexable +void promote_to_bidi_indexable(struct flexible *flex) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: promote_null_to_bidi_indexable +void promote_null_to_bidi_indexable(void) { + struct flexible *b = (struct flexible *)0; +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_null_to_single +void promote_null_to_single() { + struct flexible *__single b = (struct flexible *)0; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_b_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_to_single +void promote_to_single(struct flexible *flex) { + struct flexible *__single s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: double_promote_to_bidi_indexable +void double_promote_to_bidi_indexable(struct flexible *__sized_by(size) flex, int size) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +// CHECK-LABEL: promote_to_sized_by +void promote_to_sized_by(struct flexible *flex) { + unsigned long long siz; + struct flexible *__sized_by(siz) s; + + siz = sizeof(struct flexible) + sizeof(int) * flex->count; + s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && sizeof(struct flexible) + sizeof(int) * flex->count <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *__bidi_indexable)flex' +// CHECK: | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK: | | | |-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | | |-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + +// CHECK-LABEL: promote_to_single_assign +void promote_to_single_assign(struct flexible *flex) { + struct flexible *__single s = flex; + s->count = flex->count; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-MemberExpr {{.+}} ->count +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c new file mode 100644 index 0000000000000..96f9f3df80cee --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c @@ -0,0 +1,193 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// Make sure that calling a builtin and function has the same checks. + +// CHECK: FunctionDecl [[func_my_memset:0x[^ ]+]] {{.+}} my_memset +void *__sized_by(len) my_memset(void *__sized_by(len) b, int c, unsigned long len); + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && size <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *)flex' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*)(void *__single __sized_by(), int, unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by()':'void *__single' +void foo(flex_t *flex, unsigned size) { + __builtin_memset(flex, 0, size); +} + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && size <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *)flex' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)(*__single)(void *__single __sized_by(len), int, unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_my_memset]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +void bar(flex_t *flex, unsigned size) { + my_memset(flex, 0, size); +} diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c new file mode 100644 index 0000000000000..18d4e279e3c2d --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c @@ -0,0 +1,144 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[]; +}; + +// CHECK-LABEL: flex_no_bounds_promotion +int flex_no_bounds_promotion(struct flexible *__bidi_indexable flex) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: flex_promote_from_sized_by +int flex_promote_from_sized_by(struct flexible *__sized_by(size) flex, long size) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: flex_promote_from_single +int flex_promote_from_single(struct flexible *flex) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +struct nested_flexible { + int blah; + struct flexible flex; +}; + +// CHECK-LABEL: nested_flex_no_bounds_promotion +int nested_flex_no_bounds_promotion(struct nested_flexible *__bidi_indexable nested) { + return nested->flex.elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_nested:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_nested]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: nested_flex_promote_from_sized_by +int nested_flex_promote_from_sized_by(struct nested_flexible *__sized_by(size) nested, long size) { + return nested->flex.elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_nested_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_nested_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: nested_flex_promote_from_single +int nested_flex_promote_from_single(struct nested_flexible *nested) { + return nested->flex.elems[2]; +} +// CHECK: |-ParmVarDecl [[var_nested_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-MemberExpr {{.+}} .elems +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_nested_2]] +// CHECK: `-IntegerLiteral {{.+}} 2 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c new file mode 100644 index 0000000000000..9a67767b716ff --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c @@ -0,0 +1,195 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK: FunctionDecl [[func_return_flex:0x[^ ]+]] {{.+}} return_flex +struct flexible *return_flex(); + +// CHECK-LABEL: single_init +void single_init() { + struct flexible *__single s = return_flex(); +} +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' + + +// CHECK-LABEL: single_assign +void single_assign() { + struct flexible *__single s; + s = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: single_assign_with_count +void single_assign_with_count() { + struct flexible *__single s; + s = return_flex(); + s->count = 10; +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' + +// CHECK-LABEL: bidi_init +void bidi_init() { + struct flexible *b = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: bidi_assign +void bidi_assign() { + struct flexible *b; + b = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-BinaryOperator {{.+}} 'struct flexible *__bidi_indexable' '=' +// CHECK: |-DeclRefExpr {{.+}} [[var_b_1]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c new file mode 100644 index 0000000000000..131493f7e0d4b --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c @@ -0,0 +1,131 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: elem_access +void elem_access(struct flexible *__unsafe_indexable flex) { + flex->elems[2] = 0; // array subscription check is currently done directly in clang CodeGen. +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | `-IntegerLiteral {{.+}} 2 +// CHECK: `-IntegerLiteral {{.+}} 0 + + +// CHECK-LABEL: count_access +void count_access(struct flexible *__unsafe_indexable flex) { + flex->count = 10; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-IntegerLiteral {{.+}} 10 + + +// CHECK-LABEL: elem_access_deref +void elem_access_deref(struct flexible *__unsafe_indexable flex) { + (*flex).elems[2] = 0; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 2 +// CHECK: `-IntegerLiteral {{.+}} 0 + + +// CHECK-LABEL: count_access_deref +void count_access_deref(struct flexible *__unsafe_indexable flex) { + (*flex).count = 10; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '10 <= (*flex).count && 0 <= 10' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_7]] +// CHECK: `-IntegerLiteral {{.+}} 10 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c new file mode 100644 index 0000000000000..49b7a4a3eaf12 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c @@ -0,0 +1,247 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +int flex_no_bounds_promotion(struct flexible *__bidi_indexable flex) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_no_bounds_promotion +// CHECK: {{^}}| |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int flex_promote_from_sized_by(struct flexible *__sized_by(size) flex, long size) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_promote_from_sized_by +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int flex_promote_from_single(struct flexible *flex) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_promote_from_single +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +struct nested_flexible { + int blah; + struct flexible flex; +}; + +int nested_flex_no_bounds_promotion(struct nested_flexible *__bidi_indexable nested) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_no_bounds_promotion +// CHECK: {{^}}| |-ParmVarDecl [[var_nested:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_nested]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int nested_flex_promote_from_sized_by(struct nested_flexible *__sized_by(size) nested, long size) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_promote_from_sized_by +// CHECK: {{^}}| |-ParmVarDecl [[var_nested_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_nested_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + + +int nested_flex_promote_from_single(struct nested_flexible *nested) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_promote_from_single +// CHECK: {{^}}| |-ParmVarDecl [[var_nested_2:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_nested_2]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c b/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c new file mode 100644 index 0000000000000..9e35f8ed520ef --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c @@ -0,0 +1,317 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK: |-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz + +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: foo +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && len <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= len' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | | `-AssumptionExpr +// CHECK-NEXT: {{^}}| | | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single __sized_by(16UL + 4UL * len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK-NEXT: {{^}}| | | | |-IntegerLiteral {{.+}} 16 +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK-NEXT: {{^}}| | | | |-IntegerLiteral {{.+}} 4 +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2]] +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}}| | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' + +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} + +// CHECK-LABEL: foo2 +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-DeclStmt +// CHECK-NEXT: {{^}} | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} |-DeclStmt +// CHECK-NEXT: {{^}} | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && len <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= len' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | | `-AssumptionExpr +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single __sized_by(16UL + 4UL * len)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 16 +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 4 +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}} | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}} | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | |-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' + diff --git a/clang/test/BoundsSafety/AST/forward-declared-type.c b/clang/test/BoundsSafety/AST/forward-declared-type.c new file mode 100644 index 0000000000000..60e2a00c07f36 --- /dev/null +++ b/clang/test/BoundsSafety/AST/forward-declared-type.c @@ -0,0 +1,789 @@ + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -verify -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// CHECK: |-RecordDecl +struct unsized; + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSizedBy:0x[^ ]+]] {{.+}} unsizedSizedByToSizedBy +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToSizedBy(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_size:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-DependerDeclsAttr + int size = len; +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] + struct unsized * __single __sized_by(size) p2 = p; +} + +// CHECK: |-RecordDecl +struct Bar { +// CHECK: | |-FieldDecl +// CHECK: | | `-DependerDeclsAttr + int size; +// CHECK: | `-FieldDecl + struct unsized * __single __sized_by(size) p; +}; + +// CHECK: |-FunctionDecl [[func_structMemberUnsizedSizedBy:0x[^ ]+]] {{.+}} structMemberUnsizedSizedBy +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// expected-note@+2{{consider adding '__sized_by(len)' to 'p'}} +// expected-note@+1{{consider adding '__sized_by(10)' to 'p'}} +void structMemberUnsizedSizedBy(struct unsized * p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && 10 <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__single)p && 0 <= 10' +// CHECK: | |-InitListExpr +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct unsized *__single' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_1]] + struct Bar bar = { .size = 10, .p = p }; // expected-error{{initializing 'bar.p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') and size value of 10 with 'struct unsized *__single' and pointee of size 0 always fails}} +// CHECK: |-BinaryOperator {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .p +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct unsized *__single' + bar.p = p; // expected-warning{{size value is not statically known: assigning to 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') from 'struct unsized *__single' is invalid for any size greater than 0}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .size +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' + bar.size = len; // expected-note{{size assigned here}} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_bar2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'bar.p <= __builtin_get_pointer_upper_bound(bar.p) && __builtin_get_pointer_lower_bound(bar.p) <= bar.p && len <= (char *)__builtin_get_pointer_upper_bound(bar.p) - (char *__bidi_indexable)bar.p && 0 <= len' +// CHECK: | |-InitListExpr +// CHECK: | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .size +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-MemberExpr {{.+}} .p +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' + struct Bar bar2 = { .size = len, .p = bar.p }; +// CHECK: |-BinaryOperator {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .p +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' + bar2.p = bar.p; +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .size +// CHECK: | `-DeclRefExpr {{.+}} [[var_bar2]] +// CHECK: `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' + bar2.size = 10; +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSingle:0x[^ ]+]] {{.+}} unsizedSizedByToSingle +// CHECK: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToSingle(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | |-OpaqueValueExpr [[ove_16]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' + struct unsized * __single p2 = p; +} +// CHECK: |-RecordDecl +struct other; +// CHECK: |-FunctionDecl [[func_unsizedSingleToSingleTypecast:0x[^ ]+]] {{.+}} unsizedSingleToSingleTypecast +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +void unsizedSingleToSingleTypecast(struct unsized * p) { +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct other *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_3]] + struct other * __single p2 = p; // expected-warning{{incompatible pointer types initializing 'struct other *__single' with an expression of type 'struct unsized *__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSizedByTypecast:0x[^ ]+]] {{.+}} unsizedSizedByToSizedByTypecast +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void unsizedSizedByToSizedByTypecast(struct unsized * __sized_by(len) p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | `-DependerDeclsAttr + int size = len; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'struct other *__single __sized_by(size)':'struct other *__single' +// CHECK: | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct other *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct other *' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_21]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_18]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct other *__bidi_indexable' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | |-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_21]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_1]] + struct other * __single __sized_by(size) p2 = p; // expected-warning{{incompatible pointer types initializing 'struct other *__single' with an expression of type 'struct unsized *__single __sized_by(len)' (aka 'struct unsized *__single')}} (This warning is redundant and missing __sized_by annotation rdar://112409995) + // expected-warning@-1{{incompatible pointer types initializing 'struct other *__single __sized_by(size)' (aka 'struct other *__single') with an expression of type 'struct unsized *__single __sized_by(len)' (aka 'struct unsized *__single')}} +} + +// CHECK: |-FunctionDecl [[func_voidSizedByToVoidSizedBy:0x[^ ]+]] {{.+}} voidSizedByToVoidSizedBy +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void voidSizedByToVoidSizedBy(void * __sized_by(len) p, int len) { + int size = len; + void * __single __sized_by(size) p2 = p; +} + +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_25]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_22]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | |-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_25]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_2]] + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedNull:0x[^ ]+]] {{.+}} unsizedBidiForgedNull +void unsizedBidiForgedNull() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + struct unsized * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedDyn:0x[^ ]+]] {{.+}} unsizedBidiForgedDyn +// CHECK: | |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedBidiForgedDyn(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_6:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: | | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_5]] + struct unsized * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, p, len); +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedTypecast:0x[^ ]+]] {{.+}} unsizedBidiForgedTypecast +void unsizedBidiForgedTypecast() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_7:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct other *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + struct other * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); // expected-warning{{incompatible pointer types initializing 'struct other *__bidi_indexable' with an expression of type 'struct unsized *__bidi_indexable'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedTypecastToInt:0x[^ ]+]] {{.+}} unsizedBidiForgedTypecastToInt +void unsizedBidiForgedTypecastToInt() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_8:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + int * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); // expected-warning{{incompatible pointer types initializing 'int *__bidi_indexable' with an expression of type 'struct unsized *__bidi_indexable'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedToSizedBy:0x[^ ]+]] {{.+}} unsizedBidiForgedToSizedBy +// CHECK: |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void unsizedBidiForgedToSizedBy(struct unsized * __sized_by(len) p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | `-DependerDeclsAttr + int size = len; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_9:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} '((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) <= __builtin_get_pointer_upper_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) && __builtin_get_pointer_lower_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) <= ((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) && size <= (char *)__builtin_get_pointer_upper_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) - (char *__bidi_indexable)((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_31]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_28]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_29]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_30]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: `-OpaqueValueExpr [[ove_31]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_3]] + struct unsized * __single __sized_by(size) p2 = __unsafe_forge_bidi_indexable(struct unsized *, p, len); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedNull:0x[^ ]+]] {{.+}} unsizedSingleForgedNull +void unsizedSingleForgedNull() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_10:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 + struct unsized * __single p2 = __unsafe_forge_single(struct unsized *, 0); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedDyn:0x[^ ]+]] {{.+}} unsizedSingleForgedDyn +// CHECK: | |-ParmVarDecl [[var_p_8:0x[^ ]+]] +void unsizedSingleForgedDyn(int p) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_11:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_8]] + struct unsized * __single p2 = __unsafe_forge_single(struct unsized *, p); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToBidi:0x[^ ]+]] {{.+}} unsizedSingleForgedToBidi +// CHECK: |-ParmVarDecl [[var_p_9:0x[^ ]+]] +void unsizedSingleForgedToBidi(int p) { +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_12:0x[^ ]+]] +// CHECK: `-RecoveryExpr +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: `-ForgePtrExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ParenExpr +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_9]] + // expected-note@+1{{pointer 'p2' declared here}} + struct unsized * __bidi_indexable p2 = __unsafe_forge_single(struct unsized *, p); // expected-error{{cannot initialize indexable pointer with type 'struct unsized *__bidi_indexable' from __single pointer to incomplete type 'struct unsized *__single'; consider declaring pointer 'p2' as '__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToSizedBy:0x[^ ]+]] {{.+}} unsizedSingleForgedToSizedBy +// CHECK: |-ParmVarDecl [[var_p_10:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_7:0x[^ ]+]] +void unsizedSingleForgedToSizedBy(int p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | `-DependerDeclsAttr + int size = len; // expected-note{{size initialized here}} +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_13:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} '(struct unsized *__single)__builtin_unsafe_forge_single((p)) <= __builtin_get_pointer_upper_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) && __builtin_get_pointer_lower_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) <= (struct unsized *__single)__builtin_unsafe_forge_single((p)) && size <= (char *)__builtin_get_pointer_upper_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) - (char *__single)(struct unsized *__single)__builtin_unsafe_forge_single((p)) && 0 <= size' +// CHECK: |-ParenExpr +// CHECK: | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'struct unsized *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_33]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_32]] +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK: `-OpaqueValueExpr [[ove_33]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_4]] + struct unsized * __single __sized_by(size) p2 = __unsafe_forge_single(struct unsized *, p); // expected-warning{{size value is not statically known: initializing 'p2' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') with 'struct unsized *__single' is invalid for any size greater than 0}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToBidiVoid:0x[^ ]+]] {{.+}} unsizedSingleForgedToBidiVoid +// CHECK: | |-ParmVarDecl [[var_p_11:0x[^ ]+]] +void unsizedSingleForgedToBidiVoid(int p) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_14:0x[^ ]+]] +// CHECK: | `-RecoveryExpr +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'void *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_11]] + // expected-note@+1{{pointer 'p2' declared here}} + void * __bidi_indexable p2 = __unsafe_forge_single(void *, p); // expected-error{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'p2' as '__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToBidiVoid:0x[^ ]+]] {{.+}} unsizedSizedByToBidiVoid +// CHECK: | |-ParmVarDecl [[var_p_12:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_8:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToBidiVoid(void * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_15:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_34]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK: | | `-OpaqueValueExpr [[ove_35]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_8]] +// CHECK: | |-OpaqueValueExpr [[ove_34]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' + void * __bidi_indexable p2 = p; +} + +// CHECK: -FunctionDecl [[func_unsizedSingleToSizedByToBidiVoid:0x[^ ]+]] {{.+}} unsizedSingleToSizedByToBidiVoid +// CHECK: |-ParmVarDecl [[var_p_13:0x[^ ]+]] +void unsizedSingleToSizedByToBidiVoid(void * p) { // rdar://112462891 +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_p2_16:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && 0 <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__single)p && 0 <= 0' +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_13]] +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'void *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_36]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_13]] +// CHECK: | `-OpaqueValueExpr [[ove_37]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 0 + void * __single __sized_by(0) p2 = p; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_38]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p2_16]] +// CHECK: | `-OpaqueValueExpr [[ove_39]] +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-OpaqueValueExpr [[ove_38]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' + void * __bidi_indexable p3 = p2; +} + diff --git a/clang/test/BoundsSafety/AST/func-attributes.c b/clang/test/BoundsSafety/AST/func-attributes.c new file mode 100644 index 0000000000000..9f673e45bb3d1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-attributes.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Functions + +// CHECK: FunctionDecl {{.+}} func_proto_void_cdecl 'void (void) __attribute__((cdecl))':'void (void)' +__attribute__((cdecl)) void func_proto_void_cdecl(void); + +// CHECK: FunctionDecl {{.+}} func_proto_void_noreturn 'void (void) __attribute__((noreturn))' +__attribute__((noreturn)) void func_proto_void_noreturn(void); + +// CHECK: FunctionDecl {{.+}} func_noproto_void_cdecl 'void () __attribute__((cdecl))':'void ()' +__attribute__((cdecl)) void func_noproto_void_cdecl(); + +// CHECK: FunctionDecl {{.+}} func_noproto_void_noreturn 'void () __attribute__((noreturn))' +__attribute__((noreturn)) void func_noproto_void_noreturn(); + +// CHECK: FunctionDecl {{.+}} func_proto_ret_cdecl 'void *__single(void) __attribute__((cdecl))':'void *__single(void)' +__attribute__((cdecl)) void *func_proto_ret_cdecl(void); + +// CHECK: FunctionDecl {{.+}} func_proto_ret_noreturn 'void *__single(void) __attribute__((noreturn))' +__attribute__((noreturn)) void *func_proto_ret_noreturn(void); + +// CHECK: FunctionDecl {{.+}} func_noproto_ret_cdecl 'void *__single() __attribute__((cdecl))':'void *__single()' +__attribute__((cdecl)) void *func_noproto_ret_cdecl(); + +// CHECK: FunctionDecl {{.+}} func_noproto_ret_noreturn 'void *__single() __attribute__((noreturn))' +__attribute__((noreturn)) void *func_noproto_ret_noreturn(); + +// Function pointers + +// CHECK: VarDecl {{.+}} fptr_proto_void_cdecl 'void ((*__single))(void) __attribute__((cdecl))' +__attribute__((cdecl)) void (*fptr_proto_void_cdecl)(void); + +// CHECK: VarDecl {{.+}} fptr_proto_void_noreturn 'void (*__single)(void) __attribute__((noreturn))' +__attribute__((noreturn)) void (*fptr_proto_void_noreturn)(void); + +// CHECK: VarDecl {{.+}} fptr_noproto_void_cdecl 'void ((*__single))() __attribute__((cdecl))' +__attribute__((cdecl)) void (*fptr_noproto_void_cdecl)(); + +// CHECK: VarDecl {{.+}} fptr_noproto_void_noreturn 'void (*__single)() __attribute__((noreturn))' +__attribute__((noreturn)) void (*fptr_noproto_void_noreturn)(); + +// CHECK: VarDecl {{.+}} fptr_proto_ret_cdecl 'void *__single((*__single))(void) __attribute__((cdecl))' +__attribute__((cdecl)) void *(*fptr_proto_ret_cdecl)(void); + +// CHECK: VarDecl {{.+}} fptr_proto_ret_noreturn 'void *__single(*__single)(void) __attribute__((noreturn))' +__attribute__((noreturn)) void *(*fptr_proto_ret_noreturn)(void); + +// CHECK: VarDecl {{.+}} fptr_noproto_ret_cdecl 'void *__single((*__single))() __attribute__((cdecl))' +__attribute__((cdecl)) void *(*fptr_noproto_ret_cdecl)(); + +// CHECK: VarDecl {{.+}} fptr_noproto_ret_noreturn 'void *__single(*__single)() __attribute__((noreturn))' +__attribute__((noreturn)) void *(*fptr_noproto_ret_noreturn)(); diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c new file mode 100644 index 0000000000000..18043c24d157a --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-error=implicit-function-declaration %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-error=implicit-function-declaration -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void call_undeclared_function(int *__counted_by(count) p, int count) { + int *a = p; + undeclared_function(a); +} +// CHECK:{{.*}}warning: call to undeclared function 'undeclared_function'; ISO C99 and later do not support implicit function declarations + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_undeclared_function 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int ()' Function {{.*}} 'undeclared_function' 'int ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c new file mode 100644 index 0000000000000..fa2a7316d23df --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +// Deliberately use `__indexable` as an attribute +// because nothing else in this program uses it. +// That means when check for a cast to this pointer +// attribute in `call_has_no_prototype_k_and_r` that +// we can be sure it is being done because of the attribute +// in this function declaration and nothing else. +int *has_no_prototype_k_and_r(a) +int *__indexable a; +{ + return a; +} + +void call_has_no_prototype_k_and_r(int *__counted_by(count) p, int count) { + int *a = p; + has_no_prototype_k_and_r(a); +} + +// CHECK-LABEL:|-FunctionDecl {{.*}} has_no_prototype_k_and_r 'int *__single(int *__indexable)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} a 'int *__indexable' + +// This behavior is a little surprising. `has_no_prototype_k_and_r` does not +// have a prototype so one might expect the default argument promotion to apply. +// However, that's not what clang does because in `Sema::BuildResolvedCallExpr` +// although it figures out there's no prototype it tries looking for it +// elsewhere and thinks it finds one so it doesn't do default argument promotion. +// While this might be a violation of the C standard it means we end up casting +// to the actual types on the function which means we use the correct -fbounds-safety +// attributes. So this likely bug in Clang is actually good for BoundsSafety +// because it means there won't be an ABI mismatch. + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype_k_and_r 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int *__single(*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__single()' Function {{.*}} 'has_no_prototype_k_and_r' 'int *__single(int *__indexable)' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c new file mode 100644 index 0000000000000..da6cbee239029 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void has_no_prototype(); + +void call_has_no_prototype() { + int buffer[5] = {0}; + return has_no_prototype(buffer); +} + +// CHECK-LABEL:|-FunctionDecl {{.*}} used has_no_prototype 'void ()' +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype 'void ()' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} used buffer 'int[5]' cinit +// ... +// CHECK: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void ()' Function {{.*}} 'has_no_prototype' 'void ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}}'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int[5]' lvalue Var {{.*}} 'buffer' 'int[5]' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c new file mode 100644 index 0000000000000..a85f09d8da426 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void has_no_prototype(); + +void call_has_no_prototype(int *__counted_by(count) p, int count) { + int *a = p; + has_no_prototype(a); +} + +// CHECK:|-FunctionDecl {{.*}} has_no_prototype 'void ()' + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void ()' Function {{.*}} 'has_no_prototype' 'void ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/get-pointer-bound.c b/clang/test/BoundsSafety/AST/get-pointer-bound.c new file mode 100644 index 0000000000000..72e86f0b52e8a --- /dev/null +++ b/clang/test/BoundsSafety/AST/get-pointer-bound.c @@ -0,0 +1,79 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void bidi_indexable(int *__bidi_indexable p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt {{.+}} +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'p' 'int *__bidi_indexable' +// CHECK: | `-DeclStmt {{.+}} +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'p' 'int *__bidi_indexable' + +void fwd_indexable(int *__indexable p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + +void single(int *__single p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single' + +void array(void) { + int array[10]; + int *q = __builtin_get_pointer_lower_bound(array); + int *r = __builtin_get_pointer_upper_bound(array); +} + +// CHECK: |-FunctionDecl {{.+}} array 'void (void)' +// CHECK: | `-CompoundStmt {{.+}} +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used array 'int[10]' +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int[10]' lvalue Var {{.+}} 'array' 'int[10]' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} col:10 r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int[10]' lvalue Var {{.+}} 'array' 'int[10]' + +int endOfFile; diff --git a/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c b/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c new file mode 100644 index 0000000000000..11c97f9a75002 --- /dev/null +++ b/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c @@ -0,0 +1,33 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +#include + +extern int *__single extern_ptr; + +void foo(void) { + // Silence these expected warnings so they don't show up in AST dump. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + char *__bidi_indexable b = extern_ptr; +#pragma clang diagnostic pop +} + +// This test exists to make sure that the implementation of the +// "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" +// warning doesn't break the requirement that the above implicit conversion is +// split into a BitCast then BoundsSafetyPointerCast. + +// CHECK:|-VarDecl {{.*}} used extern_ptr 'int *__single' extern +// CHECK-NEXT:`-FunctionDecl {{.*}} foo 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} b 'char *__bidi_indexable' cinit + +// Make sure the `(int* __single) -> (char* __bidi_indexable) gets split into two +// separate implicit casts. +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__single' + +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'extern_ptr' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/implicit-thin-decls.c b/clang/test/BoundsSafety/AST/implicit-thin-decls.c new file mode 100644 index 0000000000000..b7982c0bd7dca --- /dev/null +++ b/clang/test/BoundsSafety/AST/implicit-thin-decls.c @@ -0,0 +1,35 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +int* foo(int *); +int* bar(int *parm); +int* baz(int *param) {} + +struct S { + int *i; + long *l; +}; + +typedef struct { + long *l; + void *v; +} T; + +// CHECK: |-FunctionDecl {{.+}} foo 'int *__single(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} 'int *__single' +// CHECK: |-FunctionDecl {{.+}} bar 'int *__single(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} parm 'int *__single' +// CHECK: |-FunctionDecl {{.+}} baz 'int *__single(int *__single)' +// CHECK: | |-ParmVarDecl {{.+}} param 'int *__single' +// CHECK: | `-CompoundStmt +// CHECK: |-RecordDecl {{.+}} struct S definition +// CHECK: | |-FieldDecl {{.+}} i 'int *__single' +// CHECK: | `-FieldDecl {{.+}} l 'long *__single' +// CHECK: |-RecordDecl [[ADDR:0x[a-z0-9]+]] {{.+}} struct definition +// CHECK: | |-FieldDecl {{.+}} l 'long *__single' +// CHECK: | `-FieldDecl {{.+}} v 'void *__single' +// CHECK: `-TypedefDecl {{.+}} T 'struct T':'T' +// CHECK: `-ElaboratedType {{.+}} 'struct T' sugar +// CHECK: `-RecordType {{.+}} 'T' +// CHECK: `-Record [[ADDR]] '' diff --git a/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c b/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c new file mode 100644 index 0000000000000..f55c7f08a1b86 --- /dev/null +++ b/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +#define NULL ((void *__single)0) + +// CHECK-LABEL: test_null_to_bidi 'void ()' +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} impl_bidi_ptr 'int *__bidi_indexable' cinit +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' part_of_explicit_cast +// CHECK: | `-ParenExpr {{.*}} 'void *__single' +// CHECK: | `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} impl_bidi_ptr2 'int *__bidi_indexable' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'char *__single' +// CHECK: `-ParenExpr {{.*}} 'void *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: `-IntegerLiteral {{.*}} 'int' 0 +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (void *)(char *)NULL; +} diff --git a/clang/test/BoundsSafety/AST/indexable-casts.c b/clang/test/BoundsSafety/AST/indexable-casts.c new file mode 100644 index 0000000000000..2e3d444734eb6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/indexable-casts.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O2 %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O2 %s -o /dev/null + +#include + +int foo() { + int *__indexable p; + p = (int *__indexable)(int *__bidi_indexable)p; + char *__bidi_indexable cp = (char *__bidi_indexable)p; + return 0; +} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used p 'int *__indexable' +// CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' +// CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__indexable' +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} cp 'char *__bidi_indexable' cinit +// CHECK: | `-CStyleCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'char *__indexable' part_of_explicit_cast +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' diff --git a/clang/test/BoundsSafety/AST/indexable-ptr-arith.c b/clang/test/BoundsSafety/AST/indexable-ptr-arith.c new file mode 100644 index 0000000000000..dbb921c4fc01f --- /dev/null +++ b/clang/test/BoundsSafety/AST/indexable-ptr-arith.c @@ -0,0 +1,89 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Unary operator should NOT convert __indexable to __bidi_indexable. +void unop(int *__indexable p) { + // CHECK: UnaryOperator {{.+}} 'int *__indexable' postfix '++' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + p++; + + // CHECK: UnaryOperator {{.+}} 'int *__indexable' prefix '++' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + ++p; +} + +// Binary operator should convert __indexable to __bidi_indexable. +void binop(int *__indexable p, int index) { + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)(p + index); + + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)(index + p); + + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)(p - index); + + // CHECK: BinaryOperator {{.+}} 'long' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)(p - p); +} + +// Pointer arithmetic with assignment should NOT convert __indexable to +// __bidi_indexable. +void assign(int *__indexable p, int index) { + (void)p; + + // CHECK: CompoundAssignOperator {{.+}} 'int *__indexable' '+=' ComputeLHSTy='int *__indexable' ComputeResultTy='int *__indexable' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + p += index; + + // CHECK: CompoundAssignOperator {{.+}} 'int *__indexable' '-=' ComputeLHSTy='int *__indexable' ComputeResultTy='int *__indexable' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + p -= index; +} + +// Array subscript should convert __indexable to __bidi_indexable. +void array_subscript(int *__indexable p, int index) { + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)&p[index]; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)&index[p]; +} diff --git a/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c new file mode 100644 index 0000000000000..5d299ea487990 --- /dev/null +++ b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c @@ -0,0 +1,1803 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} compound_literal_init_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c new file mode 100644 index 0000000000000..8d2b695835002 --- /dev/null +++ b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c @@ -0,0 +1,2973 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} compound_literal_init_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c b/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c new file mode 100644 index 0000000000000..6b4020668e065 --- /dev/null +++ b/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void Test() { + __asm__ ("test %0" ::"r" (("beef"))); +} +// CHECK-LABEL: Test 'void ()' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-GCCAsmStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ParenExpr {{.*}} 'char[5]' lvalue +// CHECK-NEXT: `-StringLiteral {{.*}} 'char[5]' lvalue "beef" diff --git a/clang/test/BoundsSafety/AST/macro-qualified-type.c b/clang/test/BoundsSafety/AST/macro-qualified-type.c new file mode 100644 index 0000000000000..d8cbc9a236b76 --- /dev/null +++ b/clang/test/BoundsSafety/AST/macro-qualified-type.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +#define my_unused __attribute__((__unused__)) + +// CHECK: FunctionDecl {{.+}} foo 'void (my_unused int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'my_unused int *__single':'int *__single' +// CHECK-NEXT: | `-UnusedAttr {{.+}} unused +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.+}} local 'my_unused int *__bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: `-UnusedAttr {{.+}} unused +void foo(int *_Nullable p my_unused) { + int *_Nullable local my_unused; +} diff --git a/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c b/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c new file mode 100644 index 0000000000000..4e35c8c2d8177 --- /dev/null +++ b/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void *g; + +void test() { + const void *__sized_by(4) ptr = __unsafe_forge_bidi_indexable(const void *, g, 4); +} + +// CHECK: TranslationUnitDecl +// CHECK: |-VarDecl [[var_g:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_test:0x[^ ]+]] {{.+}} test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(4)':'const void *__single' +// CHECK: | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'const void *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'const void *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g]] +// CHECK: | `-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 4 +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-IntegerLiteral {{.+}} 4 diff --git a/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c b/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c new file mode 100644 index 0000000000000..a3743cd7fa75f --- /dev/null +++ b/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c @@ -0,0 +1,45 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct foo *bar(void); +// CHECK: |-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar + +void baz(void) { + (void)bar()->p; +} + +// CHECK-LABEL: baz +// CHECK: `-CompoundStmt +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct foo *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct foo *__single(*__single)(void)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-MemberExpr {{.+}} ->p +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/mock-typedef-header.h b/clang/test/BoundsSafety/AST/mock-typedef-header.h new file mode 100644 index 0000000000000..c4fa48d5190ae --- /dev/null +++ b/clang/test/BoundsSafety/AST/mock-typedef-header.h @@ -0,0 +1,8 @@ +#ifndef __MOCK_TYPEDEF_HEADER_H__ +#define __MOCK_TYPEDEF_HEADER_H__ +#pragma clang system_header + +typedef int * siptr_t; +typedef int ** siptr_ptr_t; + +#endif diff --git a/clang/test/BoundsSafety/AST/multi-level-attr-decls.c b/clang/test/BoundsSafety/AST/multi-level-attr-decls.c new file mode 100644 index 0000000000000..76654e889eb6d --- /dev/null +++ b/clang/test/BoundsSafety/AST/multi-level-attr-decls.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable *__single ptrBoundPtrThin; + // CHECK: FieldDecl {{.+}} ptrBoundPtrThin 'int *__bidi_indexable*__single' +}; + +typedef struct Foo Foo; + +Foo *__bidi_indexable *__single Test (Foo *__single *__bidi_indexable argFooPtrThinPtrBound) { + Foo *__single *__bidi_indexable localFooPtrThinPtrBound = argFooPtrThinPtrBound; + Foo *__bidi_indexable *__single Res; + return Res; +} +// CHECK: FunctionDecl {{.+}} Test 'Foo *__bidi_indexable*__single(Foo *__single*__bidi_indexable)' +// CHECK: VarDecl {{.+}} localFooPtrThinPtrBound 'Foo *__single*__bidi_indexable' cinit +// CHECK: VarDecl {{.+}} Res 'Foo *__bidi_indexable*__single' diff --git a/clang/test/BoundsSafety/AST/multiple-dependees.c b/clang/test/BoundsSafety/AST/multiple-dependees.c new file mode 100644 index 0000000000000..eeefe6a85c5f0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/multiple-dependees.c @@ -0,0 +1,22 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x c++ %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x objective-c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x objective-c++ %s 2>&1 | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(cnt1 * 3 + cnt2 + 2) ptr; +}; + +// CHECK: RecordDecl {{.*}} struct T definition +// CHECK: |-FieldDecl {{.*}} referenced cnt1 'int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_PTR:0x[0-9a-f]+]] 0 +// CHECK: |-FieldDecl {{.*}} referenced cnt2 'int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_PTR]] 0 +// CHECK: `-FieldDecl [[FIELD_PTR]] {{.*}} ptr 'int *{{.*}}__counted_by(cnt1 * 3 + cnt2 + 2)':'int *{{.*}}' diff --git a/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c b/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c new file mode 100644 index 0000000000000..8cca54924261b --- /dev/null +++ b/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c @@ -0,0 +1,29 @@ + +// RUN: %clang_cc1 -triple armv7k -target-feature +neon -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -triple armv7k -target-feature +neon -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +// CHECK-LABEL: test +void test() { + float sign[64]; + float32x4x4_t r; + __builtin_neon_vld4q_v(&r, sign, 41); +} + +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used sign 'float[64]' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used r 'float32x4x4_t':'struct float32x4x4_t' +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*)(void *, const void *, int)' +// CHECK: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_neon_vld4q_v' 'void (void *, const void *, int)' +// CHECK: |-ImplicitCastExpr {{.*}} 'void *' +// CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' +// CHECK: | `-UnaryOperator {{.*}} 'float32x4x4_t *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.*}} 'float32x4x4_t':'struct float32x4x4_t' lvalue Var {{.*}} 'r' 'float32x4x4_t':'struct float32x4x4_t' +// CHECK: |-ImplicitCastExpr {{.*}} 'const void *' +// CHECK: | `-ImplicitCastExpr {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'float[64]' lvalue Var {{.*}} 'sign' 'float[64]' +// CHECK: `-IntegerLiteral {{.*}} 'int' 41 diff --git a/clang/test/BoundsSafety/AST/nested-struct-member-count.c b/clang/test/BoundsSafety/AST/nested-struct-member-count.c new file mode 100644 index 0000000000000..2ab5ee6ecbbb0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/nested-struct-member-count.c @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + +// CHECK: -FunctionDecl [[func_access:0x[^ ]+]] {{.+}} access +// CHECK: |-ParmVarDecl [[var_bar:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_index:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_index]] + +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} + +// CHECK: -FunctionDecl [[func_assign:0x[^ ]+]] {{.+}} assign +// CHECK: |-ParmVarDecl [[var_bar_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-MemberExpr {{.+}} .len +// CHECK: | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .len +// CHECK: | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' + diff --git a/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c b/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c new file mode 100644 index 0000000000000..d7ab4a24a5076 --- /dev/null +++ b/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +void foo(const char *); +void bar(void) { + foo(__func__); + foo(__FUNCTION__); + foo(__PRETTY_FUNCTION__); +} +// CHECK:TranslationUnitDecl {{.*}} <> +// CHECK:|-FunctionDecl {{.*}} used foo 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT:`-FunctionDecl {{.*}} bar 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-CallExpr {{.*}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-PredefinedExpr {{.*}} 'const char[4]' lvalue __func__ +// CHECK-NEXT: | `-StringLiteral {{.*}} 'const char[4]' lvalue "bar" +// CHECK-NEXT: |-CallExpr {{.*}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-PredefinedExpr {{.*}} 'const char[4]' lvalue __FUNCTION__ +// CHECK-NEXT: | `-StringLiteral {{.*}} 'const char[4]' lvalue "bar" +// CHECK-NEXT: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-PredefinedExpr {{.*}} 'const char[15]' lvalue __PRETTY_FUNCTION__ +// CHECK-NEXT: `-StringLiteral {{.*}} 'const char[15]' lvalue "void bar(void)" diff --git a/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c b/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c new file mode 100644 index 0000000000000..6d527991f69cc --- /dev/null +++ b/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c @@ -0,0 +1,127 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +void bar(void *__sized_by(len) buf, int len); + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} + +// CHECK: |-ParmVarDecl [[var_elems:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_idx:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '&elems[idx] <= __builtin_get_pointer_upper_bound(&elems[idx]) && __builtin_get_pointer_lower_bound(&elems[idx]) <= &elems[idx] && sizeof (elems[idx]) <= (char *)__builtin_get_pointer_upper_bound(&elems[idx]) - (char *)&elems[idx] && 0 <= sizeof (elems[idx])' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(len), int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | `-ArraySubscriptExpr +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ArraySubscriptExpr +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryExprOrTypeTraitExpr +// CHECK: | `-ParenExpr +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/promote-fam-align.c b/clang/test/BoundsSafety/AST/promote-fam-align.c new file mode 100644 index 0000000000000..72336c1fb10ef --- /dev/null +++ b/clang/test/BoundsSafety/AST/promote-fam-align.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +// expected-no-diagnostics + +#include +typedef unsigned char uuid_t[16]; +struct s { + int count; + uuid_t fam[__counted_by(count)]; +}; + +void promote(struct s *info) { + uuid_t *uuids = &info->fam[0]; + (void)uuids; +} + +// CHECK: `-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} promote +// CHECK: |-ParmVarDecl [[var_info:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_uuids:0x[^ ]+]] +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'uuid_t *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'uuid_t *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct s *__single' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct s *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'uuid_t *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'uuid_t *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct s *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_info]] +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-ImplicitCastExpr {{.+}} 'uuid_t *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_uuids]] \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c b/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c new file mode 100644 index 0000000000000..9c16f2de20759 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c @@ -0,0 +1,189 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +struct S { + int *__counted_by(len + 1) buf; + int len; +}; + +int foo(int *__counted_by(len) buf, int len) { +// CHECK-LABEL: FunctionDecl {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] + + struct S s = {0, -1}; +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: {{^}}| | `-BoundsCheckExpr {{.+}} +// CHECK: {{^}}| | |-InitListExpr +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 + + int *ptr = buf + 1; +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 + + ptr = s.buf + 2; +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}}| | |-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 + + return *ptr; +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-UnaryOperator {{.+}} prefix '*' +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-DeclRefExpr {{.+}} [[var_ptr]] +} + +struct S_Nullable { + int *__counted_by_or_null(len + 1) buf; + int len; +}; + +int bar(int *__counted_by_or_null(len) buf, int len) { +// CHECK-LABEL: FunctionDecl {{.+}} bar +// CHECK: {{^}} |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: {{^}} |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr + + struct S_Nullable s = {0, -1}; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-InitListExpr +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' + + int *ptr = buf + 1; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 + + ptr = s.buf + 2; +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}} | |-DeclRefExpr {{.+}} [[var_ptr_1]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} .len +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} .buf +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 2 + + return *ptr; +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} `-DeclRefExpr {{.+}} [[var_ptr_1]] +} diff --git a/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c b/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c new file mode 100644 index 0000000000000..ec910deaf3028 --- /dev/null +++ b/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c @@ -0,0 +1,29 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +int main() { + int *local; + int *copy = local; + int primitive = *local; + return primitive; +} + +// CHECK: `-FunctionDecl {{.+}} main 'int ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} copy 'int *__bidi_indexable'{{.*}} cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'local' 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used primitive 'int' cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'local' 'int *__bidi_indexable' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'primitive' 'int' \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/redundant-attrs.c b/clang/test/BoundsSafety/AST/redundant-attrs.c new file mode 100644 index 0000000000000..e94c4043c0ad1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/redundant-attrs.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s +#include + +typedef int *__bidi_indexable bidiPtr; +bidiPtr __bidi_indexable ptrBoundBound; + +#define bidiPtr2 int *__bidi_indexable +bidiPtr2 __bidi_indexable ptrBoundBound2; + +// CHECK: TypedefDecl {{.*}} referenced bidiPtr 'int *__bidi_indexable' +// CHECK-NEXT: PointerType {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK: VarDecl {{.*}} ptrBoundBound 'bidiPtr':'int *__bidi_indexable' +// CHECK-NEXT: VarDecl {{.*}} ptrBoundBound2 'int *__bidi_indexable' + +typedef const int * _Nullable __bidi_indexable my_c_ptr_nullable_bidi_t; +my_c_ptr_nullable_bidi_t __bidi_indexable def_c_nullable_bidi_ptr; +// CHECK: TypedefDecl {{.*}} referenced my_c_ptr_nullable_bidi_t 'const int *__bidi_indexable _Nullable':'const int *__bidi_indexable' +// CHECK-NEXT: AttributedType {{.*}} 'const int *__bidi_indexable _Nullable' sugar +// CHECK-NEXT: PointerType {{.*}} 'const int *__bidi_indexable' +// CHECK-NEXT: QualType {{.*}} 'const int' const +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: VarDecl {{.*}} def_c_nullable_bidi_ptr 'my_c_ptr_nullable_bidi_t':'const int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c b/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c new file mode 100644 index 0000000000000..5c264b5a578c4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_conv:0x[^ ]+]] {{.+}} conv +void conv(short x[__counted_by(m)], short y[__counted_by(m)], short m); + +void test() { + short x[16]; + short y[16]; + conv(x, y, 16); +} +// CHECK-LABEL: test 'void ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_x_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_y_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(short *__single __counted_by(m), short *__single __counted_by(m), short)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_conv]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *__single __counted_by(m)':'short *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *__single __counted_by(m)':'short *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'short' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'short *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'short *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_y_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'short' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' diff --git a/clang/test/BoundsSafety/AST/sized_by_or_null_call.c b/clang/test/BoundsSafety/AST/sized_by_or_null_call.c new file mode 100644 index 0000000000000..67041132b2d28 --- /dev/null +++ b/clang/test/BoundsSafety/AST/sized_by_or_null_call.c @@ -0,0 +1,637 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list %s | FileCheck %s + +#include + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +void foo(int *__sized_by_or_null(len) p, int len) {} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_1:0x[^ ]+]] {{.+}} caller_1 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 2 <= (char *)0 - (char *)0 && 0 <= 2' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +void caller_1() { + foo(0, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_2:0x[^ ]+]] {{.+}} caller_2 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 0 <= (char *)0 - (char *)0 && 0 <= 0' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +void caller_2() { + foo(0, 0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_3:0x[^ ]+]] {{.+}} caller_3 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_4:0x[^ ]+]] {{.+}} caller_4 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || -1 <= (char *)__builtin_get_pointer_upper_bound(&i) - (char *)&i && 0 <= -1' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_5:0x[^ ]+]] {{.+}} caller_5 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i_1:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || 2 <= (char *)__builtin_get_pointer_upper_bound(&i) - (char *)&i && 0 <= 2' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_6:0x[^ ]+]] {{.+}} caller_6 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_7:0x[^ ]+]] {{.+}} caller_7 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_8:0x[^ ]+]] {{.+}} caller_8 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +void caller_8(int *__single p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: {{^}}| |-ParmVarDecl [[var_out:0x[^ ]+]] +// CHECK: {{^}}| `-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: {{^}}| `-DependerDeclsAttr +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK: {{^}}|-FunctionDecl [[func_caller_9:0x[^ ]+]] {{.+}} caller_9 +// CHECK: {{^}}| |-ParmVarDecl [[var_out_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-CallExpr +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(*len)*__single, int *__single)' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_out_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single' +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK: {{^}}`-FunctionDecl [[func_caller_10:0x[^ ]+]] {{.+}} caller_10 +// CHECK: {{^}} |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_5:0x[^ ]+]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(*len)*__single, int *__single)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_22]] +// CHECK: {{^}} | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_23]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__single __sized_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= len' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' '=' +// CHECK: {{^}} | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}} | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | | |-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_29]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + diff --git a/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c b/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c new file mode 100644 index 0000000000000..321a2e1f1beaf --- /dev/null +++ b/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c @@ -0,0 +1,163 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s 2>&1 | FileCheck %s + +#include + +struct s_no_annot { + int len; + int *ptr; +}; + +int getlen(); +int *getptr(); + +// CHECK: |-FunctionDecl [[func_getlen:0x[^ ]+]] {{.+}} getlen +// CHECK: |-FunctionDecl [[func_getptr:0x[^ ]+]] {{.+}} getptr + +// CHECK: |-FunctionDecl [[func_test_no_annot:0x[^ ]+]] {{.+}} test_no_annot +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_s1:0x[^ ]+]] +// CHECK: | | `-InitListExpr +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single(*__single)()' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_getptr]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s2:0x[^ ]+]] +void test_no_annot() { + struct s_no_annot s1 = { + .len = getlen(), + .ptr = getptr() + }; + struct s_no_annot s2; +} + + +struct s_count_annot { + int len; + int dummy; + int *__counted_by(len) ptr; +}; + +int *__counted_by(len) getcountptr(int len); + +// CHECK: |-FunctionDecl [[func_getcountptr:0x[^ ]+]] {{.+}} getcountptr +// CHECK: | `-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_test_count_annot:0x[^ ]+]] {{.+}} test_count_annot +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_s1_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-InitListExpr +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-CallExpr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s2_1:0x[^ ]+]] +void test_count_annot() { + int arr[10]; + struct s_count_annot s1 = { + .len = 10, + .ptr = arr, + // expected-warning@+1{{initializer getlen() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .dummy = getlen() + }; + struct s_count_annot s2; +} + +struct s_range_annot { + int *end; + int dummy; + int *__ended_by(end) start; +}; + +// CHECK: FunctionDecl [[func_test_range_annot:0x[^ ]+]] {{.+}} test_range_annot +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s1_2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr +// CHECK: | |-InitListExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s2_2:0x[^ ]+]] +void test_range_annot() { + int arr[10]; + struct s_range_annot s1 = { + .end = arr + 10, + .start = arr, + // expected-warning@+1{{initializer getlen() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .dummy = getlen() + }; + struct s_count_annot s2; +} diff --git a/clang/test/BoundsSafety/AST/system-count-return.c b/clang/test/BoundsSafety/AST/system-count-return.c new file mode 100644 index 0000000000000..c20c850b34d70 --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-count-return.c @@ -0,0 +1,66 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-count-return %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-count-return %s 2>&1 | FileCheck %s + +#include +// CHECK: FunctionDecl [[func_alloc_sized_by:0x[^ ]+]] {{.+}} alloc_sized_by +// CHECK: FunctionDecl [[func_alloc_attributed:0x[^ ]+]] {{.+}} alloc_attributed + +int Test() { + int len = 16; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 16 + + int *bufAuto = alloc_sized_by(len); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufAuto:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc_sized_by]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *__bidi_indexable bufBound = alloc_attributed(sizeof(int) * 10); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufBound:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | | | |-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc_attributed]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' + + return bufBound[10]; +} diff --git a/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c new file mode 100644 index 0000000000000..eed32ffe1f8aa --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + + +// Decls in the system header don't have any attributes. + +#include "system-header-merge-bounds-attributed.h" + +// CHECK: FunctionDecl {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int **' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int *' +// CHECK: FunctionDecl {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *' +// CHECK: `-ParmVarDecl {{.+}} size 'int' +// CHECK: FunctionDecl {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} eb_p 'void *' +// CHECK: `-ParmVarDecl {{.+}} end 'void *' + + +// Check if we can override them. + +void cb_in(int *__counted_by(len) cb_in_p, int len); +void cb_out(int *__counted_by(len) *cb_out_p, int len); +void cb_out_count(int *__counted_by(*len) cb_out_len_p, int *len); +void cbn(int *__counted_by_or_null(len) cbn_p, int len); +void sb(void *__sized_by(size) sb_p, int size); +void eb(void *__ended_by(end) eb_p, void *end); + +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *{{.*}} __counted_by(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *{{.*}} __counted_by(*len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *{{.*}} __counted_by_or_null(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *{{.*}} __sized_by(size)' +// CHECK: `-ParmVarDecl {{.+}} used size 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} used eb_p 'void *{{.*}} __ended_by(end)' +// CHECK: `-ParmVarDecl {{.+}} used end 'void *{{.*}} /* __started_by(eb_p) */ ' + + +// Check if the attributes are merged. + +#include "system-header-merge-bounds-attributed.h" + +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *{{.*}} __counted_by(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *{{.*}} __counted_by(*len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *{{.*}} __counted_by_or_null(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *{{.*}} __sized_by(size)' +// CHECK: `-ParmVarDecl {{.+}} used size 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} used eb_p 'void *{{.*}} __ended_by(end)' +// CHECK: `-ParmVarDecl {{.+}} used end 'void *{{.*}} /* __started_by(eb_p) */ ' diff --git a/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h new file mode 100644 index 0000000000000..b3d0fcb78790e --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h @@ -0,0 +1,8 @@ +#pragma clang system_header + +void cb_in(int *cb_in_p, int len); +void cb_out(int **cb_out_p, int len); +void cb_out_count(int *cb_out_len_p, int *len); +void cbn(int *cbn_p, int len); +void sb(void *sb_p, int size); +void eb(void *eb_p, void *end); diff --git a/clang/test/BoundsSafety/AST/system-header-terminated-by.c b/clang/test/BoundsSafety/AST/system-header-terminated-by.c new file mode 100644 index 0000000000000..4493f0c07cb7c --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-terminated-by.c @@ -0,0 +1,9 @@ + +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include "system-header-terminated-by.h" + +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __terminated_by(0))' +// CHECK-NEXT: `-ParmVarDecl {{.+}} 'int *__single __terminated_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/system-header-terminated-by.h b/clang/test/BoundsSafety/AST/system-header-terminated-by.h new file mode 100644 index 0000000000000..f0d9b987efe1f --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-terminated-by.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +void foo(int *__null_terminated); diff --git a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c new file mode 100644 index 0000000000000..6686bea1cbccf --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c @@ -0,0 +1,39 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s + +#include +#include + +// CHECK: FunctionDecl {{.+}} myalloc +// CHECK: FunctionDecl {{.+}} myalloc +// CHECK: -AllocSizeAttr + +void Test(unsigned siz) { + void *src = myalloc(siz); +} + +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} |-ParmVarDecl [[var_siz:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} `-DeclStmt +// CHECK: {{^}} `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | `-CallExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} `-OpaqueValueExpr [[ove]] {{.*}} 'void *' diff --git a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c new file mode 100644 index 0000000000000..44d50c3bf522a --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c @@ -0,0 +1,166 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s +#include +#include + +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} memcpy +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-ParmVarDecl +// CHECK: {{^}}| `-DependerDeclsAttr + +void Test(unsigned siz) { +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} |-ParmVarDecl [[var_siz:0x[^ ]+]] + + void *src = myalloc(siz); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' + + void *dst = myalloc(siz); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_dst:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' + + memcpy(dst, src, siz); +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}} | | | `-AssumptionExpr +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-BoundsCheckExpr +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()(*__single)(void *__single __sized_by(), void *__single __sized_by(), unsigned long long)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by()':'void *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by()':'void *__single' +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-attr.c b/clang/test/BoundsSafety/AST/terminated-by-attr.c new file mode 100644 index 0000000000000..030a53cd0eb53 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-attr.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: VarDecl {{.+}} array1 'int[__terminated_by(0) 3]':'int[3]' +int array1[__null_terminated 3] = {1, 2, 0}; + +// CHECK: VarDecl {{.+}} array2 'int[__terminated_by(42) 3]':'int[3]' +int array2[__terminated_by(42) 3] = {1, 2, 42}; + +// CHECK: VarDecl {{.+}} incomplete_array1 'int[__terminated_by(0) 3]':'int[3]' +int incomplete_array1[__null_terminated] = {1, 2, 0}; + +// CHECK: VarDecl {{.+}} incomplete_array2 'int[__terminated_by(42) 3]':'int[3]' +int incomplete_array2[__terminated_by(42)] = {1, 2, 42}; + +// CHECK: VarDecl {{.+}} ptr1 'int *__single __terminated_by(0)':'int *__single' +int *__null_terminated ptr1 = array1; + +// CHECK: VarDecl {{.+}} ptr2 'int *__single __terminated_by(42)':'int *__single' +int *__terminated_by(42) ptr2 = array2; + +// CHECK: VarDecl {{.+}} ptr_array1 'int *__single __terminated_by(42)[__terminated_by(0) 3]':'int *__single __terminated_by(42)[3]' +int *__terminated_by(42) ptr_array1[__null_terminated 3] = {array2, array2, 0}; + +// CHECK: VarDecl {{.+}} ptr_ptr1 'int *__single __terminated_by(42)*__single __terminated_by(0)':'int *__single __terminated_by(42)*__single' +int *__terminated_by(42) *__null_terminated ptr_ptr1 = ptr_array1; + +// CHECK: RecordDecl {{.+}} foo +// CHECK-NEXT: FieldDecl {{.+}} array3 'int[__terminated_by(0) 2]':'int[2]' +// CHECK-NEXT: FieldDecl {{.+}} ptr3 'int *__single __terminated_by(0)':'int *__single' +struct foo { + int array3[__null_terminated 2]; + int *__null_terminated ptr3; +}; + +// CHECK: ParmVarDecl {{.+}} ptr4 'int *__single __terminated_by(0)':'int *__single' +void foo(int *__null_terminated ptr4); + +// CHECK: ParmVarDecl {{.+}} ptr_cv_nt 'int *__single __terminated_by(0)const volatile':'int *__singleconst volatile' +void quals_cv_nt(int *const volatile __null_terminated ptr_cv_nt); + +// CHECK: ParmVarDecl {{.+}} ptr_nt_cv 'int *__single __terminated_by(0)const volatile':'int *__singleconst volatile' +void quals_nt_cv(int *__null_terminated const volatile ptr_nt_cv); + +// CHECK: ParmVarDecl {{.+}} nested_ptr_cv_nt 'int *__single __terminated_by(0)const volatile *__single __terminated_by(0)const volatile':'int *__single __terminated_by(0)const volatile *__singleconst volatile' +void nested_quals_cv_nt(int *__null_terminated const volatile *const volatile __null_terminated nested_ptr_cv_nt); + +// CHECK: ParmVarDecl {{.+}} nested_ptr_nt_cv 'int *__single __terminated_by(0)const volatile *__single __terminated_by(0)const volatile':'int *__single __terminated_by(0)const volatile *__singleconst volatile' +void nested_quals_nt_cv(int *__null_terminated const volatile *__null_terminated const volatile nested_ptr_nt_cv); + +// CHECK: TypedefDecl {{.+}} my_int_t 'int *' +typedef int *my_int_t; + +// CHECK: ParmVarDecl {{.+}} typedef_nt 'int *__single __terminated_by(0)':'int *__single' +void typedef_nt(my_int_t __null_terminated typedef_nt); + +// CHECK: ParmVarDecl {{.+}} typedef_nt_c 'int *__single __terminated_by(0)const':'int *__singleconst' +void typedef_nt_c(my_int_t __null_terminated const typedef_nt_c); + +// CHECK: ParmVarDecl {{.+}} typedef_c_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void typedef_c_nt(my_int_t const __null_terminated typedef_c_nt); + +// CHECK: TypedefDecl {{.+}} my_cint_t 'int *const' +typedef int *const my_cint_t; + +// CHECK: ParmVarDecl {{.+}} ctypedef_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_nt(my_cint_t __null_terminated ctypedef_nt); + +// CHECK: ParmVarDecl {{.+}} ctypedef_nt_c 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_nt_c(my_cint_t __null_terminated const ctypedef_nt_c); + +// CHECK: ParmVarDecl {{.+}} ctypedef_c_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_c_nt(my_cint_t const __null_terminated ctypedef_c_nt); + +#define my_ptr_c_nt_t int *const __null_terminated +// CHECK: VarDecl {{.+}} def_c_nt_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +my_ptr_c_nt_t __null_terminated def_c_nt_nt; + +#define my_ptr_nt_nullable_t int *__null_terminated _Nullable +// CHECK: VarDecl {{.+}} def_nt_nullable_nt 'int *__single __terminated_by(0) _Nullable':'int *__single' +my_ptr_nt_nullable_t __null_terminated def_nt_nullable_nt; + +#define my_ptr_nullable_nt_t int *_Nullable __null_terminated +// CHECK: VarDecl {{.+}} def_nullable_nt_nt 'int *__single __terminated_by(0) _Nullable':'int *__single' +my_ptr_nullable_nt_t __null_terminated def_nullable_nt_nt; + +#define my_c_a_int_t const int __attribute__((aligned(64))) +typedef my_c_a_int_t * __attribute__((align_value(64))) _Nullable __null_terminated my_c_ptr_nullable_nt_t; +// CHECK: TypedefDecl {{.*}} referenced my_c_ptr_nullable_nt_t 'const int * __terminated_by(0) _Nullable':'const int *' +// CHECK-NEXT: |-AttributedType {{.*}} 'const int * __terminated_by(0) _Nullable' sugar +// CHECK-NEXT: | `-ValueTerminatedType {{.*}} 'const int * __terminated_by(0)' sugar +// CHECK-NEXT: | `-PointerType {{.*}} 'const int *' +// CHECK-NEXT: | `-QualType {{.*}} 'const int' const +// CHECK-NEXT: | `-BuiltinType {{.*}} 'int' +// CHECK-NEXT: |-AlignedAttr {{.*}} aligned +// CHECK-NEXT: | `-ConstantExpr {{.*}} 'int' +// CHECK-NEXT: | |-value: Int 64 +// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 64 +// CHECK-NEXT: `-AlignValueAttr {{.*}} +// CHECK-NEXT: `-ConstantExpr {{.*}} 'int' +// CHECK-NEXT: |-value: Int 64 +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 64 +// CHECK: VarDecl {{.*}} def_c_nullable_nt_nt 'const int *__single __terminated_by(0) _Nullable':'const int *__single' +my_c_ptr_nullable_nt_t __null_terminated def_c_nullable_nt_nt; + diff --git a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c new file mode 100644 index 0000000000000..8c1d35aeae98a --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +#include "terminated-by-conflicting-attrs.h" + +/* terminated-by-conflicting-attrs.h: + + void foo(int *__counted_by(len) p, int len); + void foo(int *__null_terminated p, int len); + + void bar(int *__null_terminated p, int len); + void bar(int *__counted_by(len) p, int len); + +*/ + +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' +// XXX: rdar://127827450 +// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' + +void test() { + int arr[10]; + foo(arr, 10); + + int *__null_terminated ptr = 0; + bar(ptr, 0); +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h new file mode 100644 index 0000000000000..776a970d4260a --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h @@ -0,0 +1,7 @@ +#pragma clang system_header + +void foo(int *__counted_by(len) p, int len); +void foo(int *__null_terminated p, int len); + +void bar(int *__null_terminated p, int len); +void bar(int *__counted_by(len) p, int len); diff --git a/clang/test/BoundsSafety/AST/terminated-by-decay.c b/clang/test/BoundsSafety/AST/terminated-by-decay.c new file mode 100644 index 0000000000000..6ede80f80ccf9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-decay.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// __terminated_by arrays should decay to __terminated_by __single pointers. + +// CHECK: ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int[__terminated_by(0) 3]':'int[3]' lvalue Var {{.+}} 'array' 'int[__terminated_by(0) 3]':'int[3]' +void null(void) { + int array[__null_terminated 3] = {1, 2, 0}; + (void)array; +} + +// CHECK: ImplicitCastExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int[__terminated_by(42) 3]':'int[3]' lvalue Var {{.+}} 'array2' 'int[__terminated_by(42) 3]':'int[3]' +void _42(void) { + int array2[__terminated_by(42) 3] = {1, 2, 42}; + (void)array2; +} + +// CHECK: ImplicitCastExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int[__terminated_by(0) 3]':'const int[3]' lvalue Var {{.+}} 'array' 'const int[__terminated_by(0) 3]':'const int[3]' +void quals(void) { + const int array[__null_terminated 3] = {1, 2, 0}; + (void)array; +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c b/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..7a7d22eb6e411 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-<<>> +void null(int *__indexable ptr) { + __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr_to_term' 'int *__indexable' +void null_ptr_to_term(int *__indexable ptr, int *__indexable ptr_to_term) { + __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by((42))':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-<<>> +void _42(int *__indexable ptr) { + __unsafe_terminated_by_from_indexable(42, ptr); +} + +static int array[42]; + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int[42]' lvalue Var {{.+}} 'array' 'int[42]' +// CHECK-NEXT: `-<<>> +void decay(void) { + __unsafe_null_terminated_from_indexable(array); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'const int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'const int *__indexable' +// CHECK-NEXT: `-<<>> +void quals(const int *__indexable ptr) { + __unsafe_null_terminated_from_indexable(ptr); +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-redecl.c b/clang/test/BoundsSafety/AST/terminated-by-redecl.c new file mode 100644 index 0000000000000..789c90a49d8e5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-redecl.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include "terminated-by-redecl.h" +// CHECK:|-FunctionDecl {{.*}} test_system_no_annot_argument 'void (int *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_1 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_2 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_3 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_no_annot_return 'int *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_1 'const char *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_2 'const char *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_3 'const char *()' + + +const char *test(); +const char *__null_terminated test(); +// CHECK-NEXT:|-FunctionDecl {{.*}} test 'const char *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test 'const char *__single __terminated_by(0)()' + + +void test_system_no_annot_argument(int *__null_terminated p); +void test_system_nt_argument(int *__null_terminated p); +void test_system_nt_argument_implicit_1(const char *p); +void test_system_nt_argument_implicit_2(const char *__single p); +void test_system_nt_argument_implicit_3(const char *__null_terminated p); +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_no_annot_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_1 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_2 'void (const char *__single)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_3 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single __terminated_by(0)':'const char *__single' + + +int *__null_terminated test_system_no_annot_return(); +int *__null_terminated test_system_nt_return(); +const char *test_system_nt_return_implicit_1(); +const char *__unsafe_indexable test_system_nt_return_implicit_2(); +const char *__null_terminated test_system_nt_return_implicit_3(); + +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_no_annot_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_1 'const char *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_2 'const char *__unsafe_indexable()' +// CHECK-NEXT:`-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_3 'const char *__single __terminated_by(0)()' diff --git a/clang/test/BoundsSafety/AST/terminated-by-redecl.h b/clang/test/BoundsSafety/AST/terminated-by-redecl.h new file mode 100644 index 0000000000000..d77a062761f65 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-redecl.h @@ -0,0 +1,15 @@ +#pragma clang system_header + +#include + +void test_system_no_annot_argument(int *p); +void test_system_nt_argument(int *__null_terminated p); +void test_system_nt_argument_implicit_1(const char *p); +void test_system_nt_argument_implicit_2(const char *p); +void test_system_nt_argument_implicit_3(const char *p); + +int *test_system_no_annot_return(); +int *__null_terminated test_system_nt_return(); +const char *test_system_nt_return_implicit_1(); +const char *test_system_nt_return_implicit_2(); +const char *test_system_nt_return_implicit_3(); diff --git a/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c b/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c new file mode 100644 index 0000000000000..57eaedc1f2610 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-<<>> +void null(int *__null_terminated ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: `-<<>> +void _42(int *__terminated_by(42) ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'const int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' lvalue ParmVar {{.+}} 'ptr' 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: `-<<>> +void quals(const int *__null_terminated ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-<<>> +void null_unsafe(int *__null_terminated ptr) { + __unsafe_terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void explicit_null(int *__null_terminated ptr) { + __null_terminated_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void explicit_null_unsafe(int *__null_terminated ptr) { + __unsafe_null_terminated_to_indexable(ptr); +} diff --git a/clang/test/BoundsSafety/AST/ternary-on-indexables.c b/clang/test/BoundsSafety/AST/ternary-on-indexables.c new file mode 100644 index 0000000000000..2c65a7a79c900 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ternary-on-indexables.c @@ -0,0 +1,361 @@ +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +void Test(int sel) { + int a; + int *x = &a; + int *y = &a; + int *z = sel ? x : y; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z 'int *__bidi_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + + char c; + char *x_char = &c; + char *y_char = &c; + char *z_char = sel ? x_char : y_char; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_char 'char *__bidi_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'char *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'x_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'y_char' 'char *__bidi_indexable' + + + void *__indexable x_ix_void = &a; + void *__indexable y_ix_void = &a; + void *__indexable z_ix_void = x_ix_void ?: y_ix_void; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_ix_void 'void *__indexable' cinit + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + int *__indexable x_ix = &a; + int *__indexable y_ix = &a; + int *__indexable z_ix = sel ? x_ix : y_ix; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_ix 'int *__indexable' cinit + // CHECK: | `-ConditionalOperator{{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'x_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + int *__single x_sg = &a; + int *__single y_sg = &a; + int *__single z_sg = sel ? x_sg : y_sg; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_sg 'int *__single' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + int *__unsafe_indexable x_uix = &a; + int *__unsafe_indexable y_uix = &a; + int *__unsafe_indexable z_uix = sel ? x_uix : y_uix; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_uix 'int *__unsafe_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__unsafe_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'x_uix' 'int *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y_uix' 'int *__unsafe_indexable' + + char *__unsafe_indexable x_uix_char = &c; + char *__unsafe_indexable y_uix_char = &c; + char *__unsafe_indexable z_uix_char = sel ? x_uix_char : y_uix_char; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_uix_char 'char *__unsafe_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'char *__unsafe_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'char *__unsafe_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'char *__unsafe_indexable' lvalue Var {{.*}} 'x_uix_char' 'char *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__unsafe_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'char *__unsafe_indexable' lvalue Var {{.*}} 'y_uix_char' 'char *__unsafe_indexable' + + z = sel ? x : y; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z = sel ? x : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z = x ?: y_sg; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z = x ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_ix = sel ? x_ix_void : y; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_ix = sel ? x_sg : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z_ix = x_sg ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_ix_void = sel ? x_sg : y; + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_ix_void = sel ? x_sg : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + z_ix_void = sel ? x_sg : y_sg; + #pragma clang diagnostic pop + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z_char = sel ? x_ix : y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'x_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = x_sg ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = sel ? x_sg : y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = sel ? x_ix_void : y; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_char = x_ix_void ?: y_ix; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z_char = sel ? x_ix_void : y_sg; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z_char = x_ix_void ?: y_ix_void; + // CHECK: `-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' +} diff --git a/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c b/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c new file mode 100644 index 0000000000000..a89dbad5f9564 --- /dev/null +++ b/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c @@ -0,0 +1,50 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +typedef long a; +typedef struct { + a *b; + a *c; +} d, *e; +#define f(g, h) \ + a *i; \ + d j = {&i[0], &i[h]}; \ + e g = &j; +#define k(g) sizeof(g) +#define l(g) k(g) +void m(void) { + int h; + f(g, h) l(g); +} + +// CHECK: `-FunctionDecl {{.+}} m 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used h 'int' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used i 'a *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used j 'd' cinit +// CHECK: | `-InitListExpr {{.+}} 'd' +// CHECK: | |-ImplicitCastExpr {{.+}} 'a *__single' +// CHECK: | | `-UnaryOperator {{.+}} 'a *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-ArraySubscriptExpr {{.+}} 'a':'long' lvalue +// CHECK: | | |-ImplicitCastExpr {{.+}} 'a *__bidi_indexable'{{.*}} +// CHECK: | | | `-DeclRefExpr {{.+}} 'a *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'i' 'a *__bidi_indexable' +// CHECK: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK: | `-ImplicitCastExpr {{.+}} 'a *__single' +// CHECK: | `-UnaryOperator {{.+}} 'a *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-ArraySubscriptExpr {{.+}} 'a':'long' lvalue +// CHECK: | |-ImplicitCastExpr {{.+}} 'a *__bidi_indexable'{{.*}} +// CHECK: | | `-DeclRefExpr {{.+}} 'a *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'i' 'a *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'h' 'int' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} referenced g 'struct d *__bidi_indexable'{{.*}} cinit +// CHECK: | `-UnaryOperator {{.+}} 'd *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.+}} 'd' lvalue Var {{.+}} 'j' 'd' +// CHECK: `-UnaryExprOrTypeTraitExpr {{.+}} 'unsigned long' sizeof +// CHECK: `-ParenExpr {{.+}} 'struct d *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} 'struct d *__bidi_indexable'{{.*}} 'g' 'struct d *__bidi_indexable'{{.*}} non_odr_use_unevaluated diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c new file mode 100644 index 0000000000000..e1d347d7ebcbf --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that the correct variable (var_c in AST) is used when promoting +// the result of the call f(c) to __bidi_indexable pointer p. + +const int count = 16; +// CHECK: VarDecl [[var_count:0x[^ ]+]] {{.*}} count 'const int' +// CHECK: `-IntegerLiteral {{.+}} 16 + +typedef int *__counted_by(count) cnt_t(int count); +// CHECK: TypedefDecl {{.+}} referenced cnt_t 'int *__single __counted_by(count)(int)' +// CHECK: `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: `-BuiltinType {{.+}} 'int' + +void foo(cnt_t f, int c) { + int count = 32; + int *p = f(c); +} +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.*}} foo 'void (cnt_t *__single, int)' +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] {{.*}} f 'cnt_t *__single' +// CHECK: |-ParmVarDecl [[var_c:0x[^ ]+]] {{.*}} c 'int' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_count_1:0x[^ ]+]] {{.*}} count 'int' +// CHECK: | `-IntegerLiteral {{.+}} 32 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] {{.*}} p 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_c]] +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'cnt_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c new file mode 100644 index 0000000000000..987ab634b6d1f --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c @@ -0,0 +1,262 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *__counted_by(count) cnt_t(int count); + +void test_cnt_fn(cnt_t cnt_fn) { + int *p = cnt_fn(16); +} +// CHECK: FunctionDecl [[func_test_cnt_fn:0x[^ ]+]] {{.+}} test_cnt_fn +// CHECK: |-ParmVarDecl [[var_cnt_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 16 +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'cnt_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_cnt_fn]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' + +typedef int *__counted_by(count) (*cnt_ptr_t)(int count); + +void test_cnt_fn_ptr(cnt_ptr_t cnt_fn_ptr) { + int *q = cnt_fn_ptr(32); +} +// CHECK: FunctionDecl [[func_test_cnt_fn_ptr:0x[^ ]+]] {{.+}} test_cnt_fn_ptr +// CHECK: |-ParmVarDecl [[var_cnt_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_q:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-IntegerLiteral {{.+}} 32 +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_cnt_fn_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' + +typedef void *__sized_by(size) sz_t(unsigned size); + +void test_sz_fn(sz_t sz_fn) { + void *r = sz_fn(64); +} +// CHECK: FunctionDecl [[func_test_sz_fn:0x[^ ]+]] {{.+}} test_sz_fn +// CHECK: |-ParmVarDecl [[var_sz_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_r:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 64 +// CHECK: | `-OpaqueValueExpr [[ove_4]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'sz_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sz_fn]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(size)':'void *__single' + +typedef void *__sized_by(size) (*sz_ptr_t)(unsigned size); + +void test_sz_fn_ptr(sz_ptr_t sz_fn_ptr) { + void *s = sz_fn_ptr(128); +} +// CHECK: FunctionDecl [[func_test_sz_fn_ptr:0x[^ ]+]] {{.+}} test_sz_fn_ptr +// CHECK: |-ParmVarDecl [[var_sz_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 128 +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)(*__single)(unsigned int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sz_fn_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__single __sized_by(size)':'void *__single' + +int *endptr; +// CHECK: VarDecl [[var_endptr:0x[^ ]+]] +typedef int *__ended_by(end) ent_t(int *end); + +void test_ent_fn(ent_t ent_fn) { + int *p = ent_fn(endptr); +} +// CHECK: FunctionDecl [[func_test_ent_fn:0x[^ ]+]] {{.+}} test_ent_fn +// CHECK: |-ParmVarDecl [[var_ent_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'ent_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_fn]] +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_endptr]] +// CHECK: `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single' + +typedef int *__ended_by(end) (*ent_ptr_t)(int *end); + +void test_ent_fn_ptr(ent_ptr_t ent_fn_ptr) { + int *q = ent_fn_ptr(endptr); +} +// CHECK: FunctionDecl [[func_test_ent_fn_ptr:0x[^ ]+]] {{.+}} test_ent_fn_ptr +// CHECK: |-ParmVarDecl [[var_ent_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_q_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_fn_ptr]] +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_9]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_endptr]] +// CHECK: `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single' + +typedef void ent_param_t(const char *__ended_by(end) start, const char *end); + +void test_ent_param_fn(ent_param_t ent_param_fn) { + const char array[10]; + ent_param_fn(array, array + 10); +} +// CHECK: FunctionDecl [[func_test_ent_param_fn:0x[^ ]+]] {{.+}} test_ent_param_fn +// CHECK: |-ParmVarDecl [[var_ent_param_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_array:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'ent_param_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_param_fn]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-BinaryOperator {{.+}} 'const char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' + +typedef void (*ent_param_ptr_t)(const char *__ended_by(end) start, const char *end); + +void test_ent_param_fn_ptr(ent_param_ptr_t ent_param_fn_ptr) { + const char array[10]; + ent_param_fn_ptr(array, array + 10); +} +// CHECK: FunctionDecl [[func_test_ent_param_fn_ptr:0x[^ ]+]] {{.+}} test_ent_param_fn_ptr +// CHECK: |-ParmVarDecl [[var_ent_param_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_array_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_param_fn_ptr]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] +// CHECK: | `-BinaryOperator {{.+}} 'const char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c new file mode 100644 index 0000000000000..7493e16cd27fe --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef void cb_t(int *__counted_by(count), int count); +typedef void sb_t(void *__sized_by(size), int size); +typedef void eb_t(void *__ended_by(end) start, void *end); + +typedef void (*cb_ptr_t)(int *__counted_by(count), int count); +typedef void (*sb_ptr_t)(void *__sized_by(size), int size); +typedef void (*eb_ptr_t)(void *__ended_by(end) start, void *end); + +cb_ptr_t g_cb_ptr; +sb_ptr_t g_sb_ptr; +eb_ptr_t g_eb_ptr; + +cb_t *g_ptr_cb; +sb_t *g_ptr_sb; +eb_t *g_ptr_eb; + +void foo( + cb_ptr_t a_cb_ptr, + sb_ptr_t a_sb_ptr, + eb_ptr_t a_eb_ptr, + cb_t *a_ptr_cb, + sb_t *a_ptr_sb, + eb_t *a_ptr_eb) { + cb_ptr_t l_cb_ptr; + sb_ptr_t l_sb_ptr; + eb_ptr_t l_eb_ptr; + + cb_t *l_ptr_cb; + sb_t *l_ptr_sb; + eb_t *l_ptr_eb; +} + +// CHECK: |-TypedefDecl {{.+}} cb_t 'void (int *__single __counted_by(count), int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void (int *__single __counted_by(count), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} sb_t 'void (void *__single __sized_by(size), int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __sized_by(size), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} eb_t 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-DynamicRangePointerType {{.+}} 'void *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-DynamicRangePointerType {{.+}} 'void *__single /* __started_by(start) */ ' sugar +// CHECK: | `-AttributedType {{.+}} 'void *__single' sugar +// CHECK: | `-PointerType {{.+}} 'void *__single' +// CHECK: | `-BuiltinType {{.+}} 'void' +// CHECK: |-TypedefDecl {{.+}} cb_ptr_t 'void (*)(int *__single __counted_by(count), int)' +// CHECK: | `-PointerType {{.+}} 'void (*)(int *__single __counted_by(count), int)' +// CHECK: | `-ParenType {{.+}} 'void (int *__single __counted_by(count), int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (int *__single __counted_by(count), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} sb_ptr_t 'void (*)(void *__single __sized_by(size), int)' +// CHECK: | `-PointerType {{.+}} 'void (*)(void *__single __sized_by(size), int)' +// CHECK: | `-ParenType {{.+}} 'void (void *__single __sized_by(size), int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __sized_by(size), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} eb_ptr_t 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-PointerType {{.+}} 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-ParenType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-DynamicRangePointerType {{.+}} 'void *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-DynamicRangePointerType {{.+}} 'void *__single /* __started_by(start) */ ' sugar +// CHECK: | `-AttributedType {{.+}} 'void *__single' sugar +// CHECK: | `-PointerType {{.+}} 'void *__single' +// CHECK: | `-BuiltinType {{.+}} 'void' +// CHECK: |-VarDecl {{.+}} g_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-VarDecl {{.+}} g_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-VarDecl {{.+}} g_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-VarDecl {{.+}} g_ptr_cb 'cb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_sb 'sb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_eb 'eb_t *__single' +// CHECK: `-FunctionDecl {{.+}} foo 'void (void (*__single)(int *__single __counted_by(count), int), void (*__single)(void *__single __sized_by(size), int), void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ ), cb_t *__single, sb_t *__single, eb_t *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-ParmVarDecl {{.+}} a_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-ParmVarDecl {{.+}} a_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_eb 'eb_t *__single' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_cb 'cb_t *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_sb 'sb_t *__single' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} l_ptr_eb 'eb_t *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c new file mode 100644 index 0000000000000..def47d08adb50 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c @@ -0,0 +1,138 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *__counted_by(count) cb_t(int count); +typedef void *__sized_by(size) sb_t(int size); +typedef int *__ended_by(end) eb_t(int *end); + +typedef int *__counted_by(count) (*cb_ptr_t)(int count); +typedef void *__sized_by(size) (*sb_ptr_t)(int size); +typedef int *__ended_by(end) (*eb_ptr_t)(int *end); + +cb_t g_cb; +sb_t g_sb; +eb_t g_eb; + +cb_ptr_t g_cb_ptr; +sb_ptr_t g_sb_ptr; +eb_ptr_t g_eb_ptr; + +cb_t *g_ptr_cb; +sb_t *g_ptr_sb; +eb_t *g_ptr_eb; + +void foo( + cb_t a_cb, + sb_t a_sb, + eb_t a_eb, + cb_ptr_t a_cb_ptr, + sb_ptr_t a_sb_ptr, + eb_ptr_t a_eb_ptr, + cb_t *a_ptr_cb, + sb_t *a_ptr_sb, + eb_t *a_ptr_eb) { + cb_t l_cb; + sb_t l_sb; + eb_t l_eb; + + cb_ptr_t l_cb_ptr; + sb_ptr_t l_sb_ptr; + eb_ptr_t l_eb_ptr; + + cb_t *l_ptr_cb; + sb_t *l_ptr_sb; + eb_t *l_ptr_eb; +} + +// CHECK: |-TypedefDecl {{.+}} referenced cb_t 'int *__single __counted_by(count)(int)' +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced sb_t 'void *__single __sized_by(size)(int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void *__single __sized_by(size)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced eb_t 'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __ended_by(end)(int *__single)' cdecl +// CHECK: | |-DynamicRangePointerType {{.+}} 'int *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-AttributedType {{.+}} 'int *__single' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced cb_ptr_t 'int *__single __counted_by(count)(*)(int)' +// CHECK: | `-PointerType {{.+}} 'int *__single __counted_by(count)(*)(int)' +// CHECK: | `-ParenType {{.+}} 'int *__single __counted_by(count)(int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced sb_ptr_t 'void *__single __sized_by(size)(*)(int)' +// CHECK: | `-PointerType {{.+}} 'void *__single __sized_by(size)(*)(int)' +// CHECK: | `-ParenType {{.+}} 'void *__single __sized_by(size)(int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void *__single __sized_by(size)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced eb_ptr_t 'int *__single __ended_by(end)(*)(int *__single)' +// CHECK: | `-PointerType {{.+}} 'int *__single __ended_by(end)(*)(int *__single)' +// CHECK: | `-ParenType {{.+}} 'int *__single __ended_by(end)(int *__single)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __ended_by(end)(int *__single)' cdecl +// CHECK: | |-DynamicRangePointerType {{.+}} 'int *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-AttributedType {{.+}} 'int *__single' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-FunctionDecl {{.+}} g_cb 'cb_t':'int *__single __counted_by(count)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int' +// CHECK: |-FunctionDecl {{.+}} g_sb 'sb_t':'void *__single __sized_by(size)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int' +// CHECK: |-FunctionDecl {{.+}} g_eb 'eb_t':'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int *__single' +// CHECK: |-VarDecl {{.+}} g_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-VarDecl {{.+}} g_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-VarDecl {{.+}} g_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-VarDecl {{.+}} g_ptr_cb 'cb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_sb 'sb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_eb 'eb_t *__single' +// CHECK: `-FunctionDecl {{.+}} foo 'void (cb_t *__single, sb_t *__single, eb_t *__single, int *__single __counted_by(count)(*__single)(int), void *__single __sized_by(size)(*__single)(int), int *__single __ended_by(end)(*__single)(int *__single), cb_t *__single, sb_t *__single, eb_t *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_eb 'eb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-ParmVarDecl {{.+}} a_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-ParmVarDecl {{.+}} a_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_eb 'eb_t *__single' +// CHECK: `-CompoundStmt {{.+}} +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_cb 'cb_t':'int *__single __counted_by(count)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int' +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_sb 'sb_t':'void *__single __sized_by(size)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int' +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_eb 'eb_t':'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} implicit 'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_cb 'cb_t *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_sb 'sb_t *__single' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} l_ptr_eb 'eb_t *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c b/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c new file mode 100644 index 0000000000000..92a0f1a8224f7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *(*fp_proto_t)(int *p); +typedef int *(*fp_proto_va_t)(int *p, ...); +typedef int *(*fp_no_proto_t)(); + +// CHECK: VarDecl {{.+}} g_var_proto 'int *__single(*__single)(int *__single)' +fp_proto_t g_var_proto; + +// CHECK: VarDecl {{.+}} g_var_proto_va 'int *__single(*__single)(int *__single, ...)' +fp_proto_va_t g_var_proto_va; + +// CHECK: VarDecl {{.+}} g_var_no_proto 'int *__single(*__single)()' +fp_no_proto_t g_var_no_proto; + +void foo(void) { + // CHECK: VarDecl {{.+}} l_var_proto 'int *__single(*__single)(int *__single)' + fp_proto_t l_var_proto; + + // CHECK: VarDecl {{.+}} l_var_proto_va 'int *__single(*__single)(int *__single, ...)' + fp_proto_va_t l_var_proto_va; + + // CHECK: VarDecl {{.+}} l_var_no_proto 'int *__single(*__single)()' + fp_no_proto_t l_var_no_proto; +} + +// CHECK: ParmVarDecl {{.+}} param_proto 'int *__single(*__single)(int *__single)' +void f1(fp_proto_t param_proto); + +// CHECK: ParmVarDecl {{.+}} param_proto_va 'int *__single(*__single)(int *__single, ...)' +void f2(fp_proto_va_t param_proto_va); + +// CHECK: ParmVarDecl {{.+}} param_no_proto 'int *__single(*__single)()' +void f3(fp_no_proto_t param_no_proto); + +// CHECK: FunctionDecl {{.+}} ret_proto 'int *__single(*__single(void))(int *__single)' +fp_proto_t ret_proto(void); + +// CHECK: FunctionDecl {{.+}} ret_proto_va 'int *__single(*__single(void))(int *__single, ...)' +fp_proto_va_t ret_proto_va(void); + +// CHECK: FunctionDecl {{.+}} ret_no_proto 'int *__single(*__single(void))()' +fp_no_proto_t ret_no_proto(void); + +struct bar { + // CHECK: FieldDecl {{.+}} field_proto 'int *__single(*__single)(int *__single)' + fp_proto_t field_proto; + + // CHECK: FieldDecl {{.+}} field_proto_va 'int *__single(*__single)(int *__single, ...)' + fp_proto_va_t field_proto_va; + + // CHECK: FieldDecl {{.+}} field_no_proto 'int *__single(*__single)()' + fp_no_proto_t field_no_proto; +}; + +typedef fp_proto_t (*nested_t)(fp_proto_va_t p1, fp_no_proto_t p2); + +// CHECK: VarDecl {{.+}} g_nested 'int *__single(*__single(*__single)(int *__single(*__single)(int *__single, ...), int *__single(*__single)()))(int *__single)' +nested_t g_nested; + +typedef int *(f_proto_t)(int *p); +typedef int *(f_proto_va_t)(int *p, ...); +typedef int *(f_no_proto_t)(); + +// CHECK: FunctionDecl {{.+}} f_proto 'int *__single(int *__single)' +f_proto_t f_proto; + +// CHECK: FunctionDecl {{.+}} f_proto_va 'int *__single(int *__single, ...)' +f_proto_va_t f_proto_va; + +// CHECK: FunctionDecl {{.+}} f_no_proto 'int *__single()' +f_no_proto_t f_no_proto; diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c new file mode 100644 index 0000000000000..ec76cb6f9e3e3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c @@ -0,0 +1,109 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int * unspecified_ptr_t; +typedef int *__single single_ptr_t; +typedef int *__bidi_indexable bidi_ptr_t; +typedef int *__unsafe_indexable unsafe_ptr_t; +typedef int *__unsafe_indexable* unsafe_ptr_ptr_t; +typedef single_ptr_t * single_ptr_ptr_t; +typedef unspecified_ptr_t * unspecified_ptr_ptr_t; +typedef int *__null_terminated term_ptr_t; + +unspecified_ptr_t g_single1; +single_ptr_t g_single2; +bidi_ptr_t g_bidi; +unsafe_ptr_t g_unsafe; +unspecified_ptr_ptr_t g_single_single; +term_ptr_t g_term; + +void foo(unspecified_ptr_t a_single1, + single_ptr_t a_single2, + bidi_ptr_t a_bidi, + unsafe_ptr_t a_unsafe, + term_ptr_t a_term) +{ + unspecified_ptr_t l_bidi1; + single_ptr_t l_single; + bidi_ptr_t l_bidi2; + unsafe_ptr_t l_unsafe; + + unspecified_ptr_t __single l_single2; + unspecified_ptr_t __unsafe_indexable l_unsafe2; + + unsafe_ptr_ptr_t l_unsafe_bidi; + single_ptr_ptr_t l_single_bidi1; + unspecified_ptr_ptr_t l_single_bidi2; + + // initialize to avoid 'must be initialized error' + term_ptr_t l_term = a_term; +} + +// CHECK: |-TypedefDecl {{.*}} referenced unspecified_ptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced single_ptr_t 'int *__single' +// CHECK: | `-PointerType {{.*}} 'int *__single' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced bidi_ptr_t 'int *__bidi_indexable' +// CHECK: | `-PointerType {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unsafe_ptr_t 'int *__unsafe_indexable' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unsafe_ptr_ptr_t 'int *__unsafe_indexable*' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable*' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced single_ptr_ptr_t 'single_ptr_t *' +// CHECK: | `-PointerType {{.*}} 'single_ptr_t *' +// CHECK: | `-TypedefType {{.*}} 'single_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'single_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *__single' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unspecified_ptr_ptr_t 'unspecified_ptr_t *' +// CHECK: | `-PointerType {{.*}} 'unspecified_ptr_t *' +// CHECK: | `-TypedefType {{.*}} 'unspecified_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'unspecified_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} term_ptr_t 'int * __terminated_by(0)':'int *' +// CHECK: | `-ValueTerminatedType {{.*}} 'int * __terminated_by(0)' sugar +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-VarDecl {{.*}} g_single1 'int *__single' +// CHECK: |-VarDecl {{.*}} g_single2 'single_ptr_t':'int *__single' +// CHECK: |-VarDecl {{.*}} g_bidi 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-VarDecl {{.*}} g_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-VarDecl {{.*}} g_single_single 'int *__single*__single' +// CHECK: |-VarDecl {{.*}} g_term 'int *__single __terminated_by(0)':'int *__single' +// CHECK: `-FunctionDecl {{.*}} foo 'void (int *__single, single_ptr_t, bidi_ptr_t, unsafe_ptr_t, int *__single __terminated_by(0))' +// CHECK: |-ParmVarDecl {{.*}} a_single1 'int *__single' +// CHECK: |-ParmVarDecl {{.*}} a_single2 'single_ptr_t':'int *__single' +// CHECK: |-ParmVarDecl {{.*}} a_bidi 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-ParmVarDecl {{.*}} a_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-ParmVarDecl {{.*}} a_term 'int *__single __terminated_by(0)':'int *__single' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_bidi1 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single 'single_ptr_t':'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_bidi2 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single2 'unspecified_ptr_t __single':'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe2 'unspecified_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe_bidi 'int *__unsafe_indexable*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single_bidi1 'single_ptr_t *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single_bidi2 'int *__single*__bidi_indexable' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} l_term 'int *__single __terminated_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c new file mode 100644 index 0000000000000..75c0d82a43cf5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c @@ -0,0 +1,47 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -verify %s 2>&1 | FileCheck %s + +#include + +typedef char * cptr_t; +typedef long long int64_t; + +void test_fdecl1(cptr_t __counted_by(len), int len); +// CHECK: FunctionDecl {{.*}} test_fdecl1 'void (char *__single __counted_by(len), int)' +// CHECK: |-ParmVarDecl {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: `-ParmVarDecl {{.*}} used len 'int' +// CHECK: `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 + + +void test_fdecl2(unsigned size, cptr_t __sized_by(size)); +// CHECK: FunctionDecl {{.*}} test_fdecl2 'void (unsigned int, char *__single __sized_by(size))' +// CHECK: |-ParmVarDecl {{.*}} used size 'unsigned int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK: `-ParmVarDecl {{.*}} 'char *__single __sized_by(size)':'char *__single' + +void test_fdecl_err1(int64_t __counted_by(len), int len); +// expected-error@-1{{'__counted_by' attribute only applies to pointer arguments}} + +int glen; +void test_fdecl_err2(cptr_t __counted_by(glen)); +// expected-error@-1{{count expression in function declaration may only reference parameters of that function}} + +struct test_count_fields { + cptr_t __sized_by(len) ptr; + int64_t len; +}; +// CHECK: RecordDecl {{.*}} struct test_count_fields definition +// CHECK: |-FieldDecl {{.*}} ptr 'char *__single __sized_by(len)':'char *__single' +// CHECK: `-FieldDecl {{.*}} referenced len 'int64_t':'long long' +// CHECK: `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 + +struct test_range_fields { + cptr_t __ended_by(end) iter; + cptr_t __ended_by(iter) start; + cptr_t end; +}; +// CHECK: RecordDecl {{.*}} struct test_range_fields definition +// CHECK: FieldDecl {{.*}} referenced iter 'char *__single __ended_by(end) /* __started_by(start) */ ':'char *__single' +// CHECK: FieldDecl {{.*}} referenced start 'char *__single __ended_by(iter)':'char *__single' +// CHECK: FieldDecl {{.*}} referenced end 'char *__single /* __started_by(iter) */ ':'char *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c new file mode 100644 index 0000000000000..87ec3665982bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c @@ -0,0 +1,73 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include "mock-typedef-header.h" + +typedef int * giptr_t; +typedef int ** giptr_ptr_t; +typedef siptr_t * gptr_siptr_t; + +giptr_t gip; +giptr_ptr_t gipp; +gptr_siptr_t gsipp; + +void foo() { + giptr_t gip_local; + giptr_ptr_t gipp_local; + + typedef char ** lcptr_ptr_t; + lcptr_ptr_t lcptr_local; + + siptr_t sip_local; + + typedef int ** giptr_ptr_t; + giptr_ptr_t gipp_local_redecl; +} + +// CHECK: |-TypedefDecl {{.*}} referenced siptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} siptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced giptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced giptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} gptr_siptr_t 'siptr_t *' +// CHECK: | `-PointerType {{.*}} 'siptr_t *' +// CHECK: | `-TypedefType {{.*}} 'siptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'siptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-VarDecl {{.*}} gip 'int *__single' +// CHECK: |-VarDecl {{.*}} gipp 'int *__single*__single' +// CHECK: |-VarDecl {{.*}} gsipp 'int *__single*__single' +// CHECK: `-FunctionDecl {{.*}} foo 'void ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} gip_local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} gipp_local 'int *__single*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-TypedefDecl {{.*}} referenced lcptr_ptr_t 'char **' +// CHECK: | `-PointerType {{.*}} 'char **' +// CHECK: | `-PointerType {{.*}} 'char *' +// CHECK: | `-BuiltinType {{.*}} 'char' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} lcptr_local 'char *__single*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} sip_local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-TypedefDecl {{.*}} referenced giptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} gipp_local_redecl 'int *__single*__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/typedef-quals.c b/clang/test/BoundsSafety/AST/typedef-quals.c new file mode 100644 index 0000000000000..c187d321e5e94 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-quals.c @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *my_ptr_t; +typedef my_ptr_t const my_cptr_t; + +typedef int *__indexable my_iptr_t; +typedef my_iptr_t const my_ciptr_t; + +void foo(void) { + my_ptr_t bp1; + my_cptr_t bp2; + const my_ptr_t bp3; + + my_iptr_t ip1; + my_ciptr_t ip2; + const my_iptr_t ip3; +} + +// CHECK: |-TypedefDecl {{.*}} referenced my_ptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_cptr_t 'const my_ptr_t':'int *const' +// CHECK: | `-QualType {{.*}} 'const my_ptr_t' const +// CHECK: | `-TypedefType {{.*}} 'my_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'my_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_iptr_t 'int *__indexable' +// CHECK: | `-PointerType {{.*}} 'int *__indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_ciptr_t 'const my_iptr_t':'int *__indexableconst' +// CHECK: | `-QualType {{.*}} 'const my_iptr_t' const +// CHECK: | `-TypedefType {{.*}} 'my_iptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'my_iptr_t' +// CHECK: | `-PointerType {{.*}} 'int *__indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: `-FunctionDecl {{.*}} foo 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp1 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp2 'int *__bidi_indexableconst' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp3 'int *__bidi_indexableconst' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} ip1 'my_iptr_t':'int *__indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} ip2 'my_ciptr_t':'int *__indexableconst' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} ip3 'const my_iptr_t':'int *__indexableconst' diff --git a/clang/test/BoundsSafety/AST/unify-auto.cpp b/clang/test/BoundsSafety/AST/unify-auto.cpp new file mode 100644 index 0000000000000..f54baf02cd333 --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-auto.cpp @@ -0,0 +1,27 @@ + +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental -verify %s | FileCheck %s + +#include + +typedef char * __terminated_by('\0') A; +typedef char * __terminated_by((char)0) B; + +A funcA(); +B funcB(); + +// CHECK: |-FunctionDecl {{.*}} testFunc 'char *__single __terminated_by('\x00')(int)' +auto testFunc(int pred) { + if (pred) return funcA(); + return funcB(); +} + +// CHECK: |-FunctionDecl {{.*}} testFunc2 'char *__single __counted_by(10)*__single(int, char *__single __counted_by(10)*__single, char *__single __counted_by(10)*__single)' +auto testFunc2(int pred, char * __counted_by(10) * c, char * __counted_by(10) * d) { + if (pred) return c; + return d; +} + +auto testFunc3(int pred, char * __counted_by(7) * c, char * __counted_by(10) * d) { + if (pred) return c; + return d; // expected-error{{'auto' in return type deduced as 'char *__single __counted_by(10)*__single' (aka 'char *__single*__single') here but deduced as 'char *__single __counted_by(7)*__single' (aka 'char *__single*__single') in earlier return statement}} +} diff --git a/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c b/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c new file mode 100644 index 0000000000000..0c3ac25905d4d --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c @@ -0,0 +1,578 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s | FileCheck %s +#include + +typedef char * __terminated_by('\0') A; +typedef char * __terminated_by((char)0) B; + +A funcA(); +B funcB(); + +char testFunc(int pred) { + A foo = pred ? funcA() : funcB(); + return foo[0]; +} + +// CHECK: |-FunctionDecl [[func_funcA:0x[^ ]+]] {{.+}} funcA +// CHECK: |-FunctionDecl [[func_funcB:0x[^ ]+]] {{.+}} funcB +// CHECK: |-FunctionDecl [[func_testFunc:0x[^ ]+]] {{.+}} testFunc +// CHECK: | |-ParmVarDecl [[var_pred:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}}'char *__single __terminated_by('\x00')':'char *__single' +// bHECK: | | |-ImplicitCastExpr {{.+}} 'bool' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred]] +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by('\x00')(*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_funcA]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by((char)0)(*__single)()' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_funcB]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_foo]] +// CHECK: | `-IntegerLiteral {{.+}} 0 + +char testFunc2(int pred, char * __counted_by(cc) c, char * __single d, int cc, int dc) { + char * __counted_by(5) foo = pred ? c : d; + return foo[3]; +} +// CHECK: |-FunctionDecl [[func_testFunc2:0x[^ ]+]] {{.+}} testFunc2 +// CHECK: | |-ParmVarDecl [[var_pred_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_cc:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_dc:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_pred_1]] +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_c]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_cc]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_d]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_foo_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 3 + +char testFunc3(int pred, char * __ended_by(*f) * e, char * __ended_by(g) * f, char * g) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __ended_by(*f)*__single' (aka 'char *__single*__single') and 'char *__single __ended_by(g) /* __started_by(*e) */ *__single' (aka 'char *__single*__single')}} + char ** foo = pred ? e : f; + const char * const bar = *foo; + return bar[2]; +} + +// CHECK: |-FunctionDecl [[func_testFunc3:0x[^ ]+]] {{.+}} testFunc3 +// CHECK: | |-ParmVarDecl [[var_pred_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_e:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_g:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_2:0x[^ ]+]] +// CHECK: | | `-RecoveryExpr +// CHECK: | | |-DeclRefExpr {{.+}} [[var_pred_2]] +// CHECK: | | |-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-DeclRefExpr {{.+}} [[var_foo_2]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +char externFunc(char * __counted_by(8)); + +// CHECK: |-FunctionDecl [[func_externFunc:0x[^ ]+]] {{.+}} externFunc + +char testFunc4(int pred, char * __counted_by(7) * c, char * __counted_by(10) * d) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __counted_by(7)*__single' (aka 'char *__single*__single') and 'char *__single __counted_by(10)*__single' (aka 'char *__single*__single')}} + return externFunc(*(pred ? c : d)); +} + +// CHECK: |-FunctionDecl [[func_testFunc4:0x[^ ]+]] {{.+}} testFunc4 +// CHECK: | |-ParmVarDecl [[var_pred_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ParenExpr +// CHECK: | `-RecoveryExpr +// CHECK: | |-DeclRefExpr {{.+}} [[var_pred_3]] +// CHECK: | |-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_1]] + +char testFunc5(int pred, char * __bidi_indexable * __counted_by(7) c, char * __bidi_indexable * __counted_by(10) d) { + return externFunc((pred ? c : d)[8]); +} + +// CHECK: |-FunctionDecl [[func_testFunc5:0x[^ ]+]] {{.+}} testFunc5 +// CHECK: | |-ParmVarDecl [[var_pred_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(8))' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(8)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ArraySubscriptExpr +// CHECK: | | | |-ParenExpr +// CHECK: | | | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_pred_4]] +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_c_2]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 7 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_d_2]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | `-IntegerLiteral {{.+}} 8 +// CHECK: | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 8 +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'long' + +char testFunc6(int pred, char * __bidi_indexable * __indexable c, char * __bidi_indexable * __counted_by(10) d) { + char * __bidi_indexable *__indexable tmp = pred ? c : d; + return externFunc(tmp[11]); +} + +// CHECK: |-FunctionDecl [[func_testFunc6:0x[^ ]+]] {{.+}} testFunc6 +// CHECK: | |-ParmVarDecl [[var_pred_5:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_5]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_3]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_d_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(8))' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(8)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ArraySubscriptExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_tmp]] +// CHECK: | | | `-IntegerLiteral {{.+}} 11 +// CHECK: | | `-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 8 +// CHECK: | |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_19]] {{.*}} 'long' + +char testFunc7(int pred, char * __unsafe_indexable * __single c, char * __single * __indexable d) { + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'char *__unsafe_indexable*__indexable' and 'char *__single*__indexable'; use explicit casts to perform this conversion}} + char * __unsafe_indexable *__indexable tmp = pred ? c : d; + return tmp[9][2]; +} + +char testFunc8(int pred, char * __ended_by(g) e, char * g) { + // expected-error@+1{{local variable end must be declared right next to its dependent decl}} + char *end = g; + // expected-error@+2{{local variable foo must be declared right next to its dependent decl}} + // expected-note@+1 2 {{previous use is here}} + char * __ended_by(end) foo = e; + // expected-error@+3{{local variable foo2 must be declared right next to its dependent decl}} + // expected-error@+2{{variable 'end' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + // expected-note@+1{{previous use is here}} + char * __ended_by(end) foo2 = foo + 1; + // expected-error@+2{{local variable bar must be declared right next to its dependent decl}} + // expected-error@+1 2 {{variable 'end' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + char * __ended_by(end) bar = *(pred ? &foo : &foo2); + return bar[0]; +} + +char testFunc9(int pred, char * __counted_by(cc) c, char * __ended_by(e) d, int cc, char *e) { + char *foo = pred ? c : d; + return foo[3]; +} + +// CHECK: -FunctionDecl [[func_testFunc9:0x[^ ]+]] {{.+}} testFunc9 +// CHECK: |-ParmVarDecl [[var_pred_8:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_cc_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_e_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_4:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'char *__bidi_indexable' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_8]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_27]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_c_5]] +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_cc_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_27]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_d_5]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(d) */ ':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(e)':'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_5]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_4]] +// CHECK: `-IntegerLiteral {{.+}} 3 + +// expected-error@+2{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} +char testFunc10(int pred, char * __counted_by(5) * __counted_by(cc) c, char * __ended_by(e) * __ended_by(f) d, int cc, char *e, char *f) { + char **foo = pred ? c : d; + // expected-error@+1{{array subscript on single pointer 'foo[3]' must use a constant index of 0 to be in bounds}} + return foo[3][2]; +} + +char testFunc11(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char *__terminated_by(0x40) *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable') and 'char *__single __terminated_by(64)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable')}} + char *__null_terminated *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +char testFunc12(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char * *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable') and 'char *__single*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable')}} + char *__single *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +char testFunc13(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + char *__null_terminated *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +// CHECK: -FunctionDecl [[func_testFunc13:0x[^ ]+]] {{.+}} testFunc13 +// CHECK: |-ParmVarDecl [[var_pred_12:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_9:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_9:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_cc_5:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_e_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_8:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_12]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_c_9]] +// CHECK: | | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_cc_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +// CHECK: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_d_9]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(d) */ ':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __ended_by(e)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_9]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)':'char *__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)':'char *__single __terminated_by(0)*__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_foo_8]] + +char testFunc14(int pred, const char *__single _Nullable __null_terminated c, const char * _Nullable __null_terminated d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: |-FunctionDecl [[func_testFunc14:0x[^ ]+]] {{.+}} testFunc14 +// CHECK: | |-ParmVarDecl [[var_pred_13:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_10:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_10:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_9:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_13]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_10]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_d_10]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_9]] + +char testFunc15(int pred, const char *__single _Nullable __null_terminated c, const char * _Nullable d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: |-FunctionDecl [[func_testFunc15:0x[^ ]+]] {{.+}} testFunc15 +// CHECK: | |-ParmVarDecl [[var_pred_14:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_11:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_11:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_10:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_14]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_11]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_d_11]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_10]] + +char testFunc16(int pred, const char * _Nullable __null_terminated c, const char * _Nullable d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: -FunctionDecl [[func_testFunc16:0x[^ ]+]] {{.+}} testFunc16 +// CHECK: |-ParmVarDecl [[var_pred_15:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_12:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_12:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_11:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_15]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_c_12]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_12]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_foo_11]] + diff --git a/clang/test/BoundsSafety/AST/unify-function-types.c b/clang/test/BoundsSafety/AST/unify-function-types.c new file mode 100644 index 0000000000000..9d5cba3e408f6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-function-types.c @@ -0,0 +1,260 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify=c %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental -verify=cpp %s +#include + +char funcA(char buf[__counted_by(len)], int len, int len2); +char funcB(char arr[__counted_by(size)], int size, int size2); +char funcC(char arr[__counted_by(size2)], int size, int size2); + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_funcA:0x[^ ]+]] {{.+}} funcA +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_len2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcB:0x[^ ]+]] {{.+}} funcB +// CHECK: | |-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcC:0x[^ ]+]] {{.+}} funcC +// CHECK: | |-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_size2_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr + +char test1(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + return (src_len % 2 == 0 ? funcA : funcB)(src_buf, src_len, src_len2); +} + +// CHECK: |-FunctionDecl [[func_test1:0x[^ ]+]] {{.+}} test1 +// CHECK: | |-ParmVarDecl [[var_src_buf:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_src_len2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ParenExpr +// CHECK: | | | | | `-ConditionalOperator {{.+}} 'char (*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int' '%' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 2 +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_funcA]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(size), int, int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_funcB]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_src_buf]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_src_len2]] +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' + + +char test2(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + // c-error@+2{{conditional expression evaluates functions with incompatible bound attributes 'char (*__single)(char *__single __counted_by(len), int, int)' (aka 'char (*__single)(char *__single, int, int)') and 'char (*__single)(char *__single __counted_by(size2), int, int)' (aka 'char (*__single)(char *__single, int, int)')}} + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char (char *__single __counted_by(len), int, int)' (aka 'char (char *__single, int, int)') and 'char (char *__single __counted_by(size2), int, int)' (aka 'char (char *__single, int, int)')}} + return (src_len % 2 == 0 ? funcA : funcC)(src_buf, src_len, src_len2); +} + +char * __counted_by(len) funcD(char buf[__counted_by(len)], int len, int len2); +char * __counted_by(size) funcE(char arr[__counted_by(size)], int size, int size2); +char * __counted_by(size2) funcF(char arr[__counted_by(size)], int size, int size2); + +// CHECK: |-FunctionDecl [[func_funcD:0x[^ ]+]] {{.+}} funcD +// CHECK: | |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_len2_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcE:0x[^ ]+]] {{.+}} funcE +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2_2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcF:0x[^ ]+]] {{.+}} funcF +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2_3:0x[^ ]+]] + +char * __counted_by(src_len) test3(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + return (src_len % 2 == 0 ? funcD : funcE)(src_buf, src_len, src_len2); +} + +// CHECK: |-FunctionDecl [[func_test3:0x[^ ]+]] {{.+}} test3 +// CHECK: | |-ParmVarDecl [[var_src_buf_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_src_len2_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_src_buf_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len2_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ParenExpr +// CHECK: | | | | `-ConditionalOperator {{.+}} 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '%' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 2 +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_funcD]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(size)(*__single)(char *__single __counted_by(size), int, int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_funcE]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__single __counted_by(len)':'char *__single' + +char test4(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + // c-error@+2{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' (aka 'char *__single(*__single)(char *__single, int, int)') and 'char *__single __counted_by(size2)(*__single)(char *__single __counted_by(size), int, int)' (aka 'char *__single(*__single)(char *__single, int, int)')}} + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(char *__single __counted_by(len), int, int)' (aka 'char *__single(char *__single, int, int)') and 'char *__single __counted_by(size2)(char *__single __counted_by(size), int, int)' (aka 'char *__single(char *__single, int, int)')}} + return *(src_len % 2 == 0 ? funcD : funcF)(src_buf, src_len, src_len2); +} + +#ifdef __cplusplus +char * __counted_by(src_len) test5(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + auto &funcRefD (funcD); + auto &funcRefE (funcE); + return (src_len % 2 == 0 ? funcRefD : funcRefE)(src_buf, src_len, src_len2); +} + +char * __counted_by(src_len) test6(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + auto &funcRefD (funcD); + auto &funcRefF (funcF); + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(char *__single __counted_by(len), int, int)' (aka 'char *__single(char *__single, int, int)') and 'char *__single __counted_by(size2)(char *__single __counted_by(size), int, int)' (aka 'char *__single(char *__single, int, int)')}} + return (src_len % 2 == 0 ? funcRefD : funcRefF)(src_buf, src_len, src_len2); +} + +#endif diff --git a/clang/test/BoundsSafety/AST/vararg-unsafe.c b/clang/test/BoundsSafety/AST/vararg-unsafe.c new file mode 100644 index 0000000000000..1ffe5f52ec234 --- /dev/null +++ b/clang/test/BoundsSafety/AST/vararg-unsafe.c @@ -0,0 +1,225 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple x86_64 -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_ARRAY +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple i686 %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_STRUCT +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm-apple-watchos -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_VOID_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm64-apple-macosx %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm64-apple-ios %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple x86_64 -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_ARRAY +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple i686 %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_STRUCT +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm-apple-watchos -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_VOID_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm64-apple-macosx %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm64-apple-ios %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR + +// expected-no-diagnostics + +#include +#include + +void baz(unsigned int n, va_list argp) { + int *__unsafe_indexable u = 0; + u = va_arg(argp, int *); + va_end(argp); +} + +void foo(unsigned int n, ...) { + int *__unsafe_indexable u = 0; + va_list argp; + va_start(argp, n); + baz(n, argp); + u = va_arg(argp, int *); + va_end(argp); +} + +typedef void (*f_t)(unsigned int n, va_list args); + +void test_va_list_on_fp(unsigned int n, va_list args) { + f_t f = baz; + f(n, args); +} + +void bar(void) { + int arr[10] = { 0 }; + int *p; + foo(1, p, arr); +} + +// VALIST_ARRAY: |-FunctionDecl {{.*}} used baz 'void (unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | |-ParmVarDecl {{.*}} used argp 'struct __va_list_tag *' +// VALIST_ARRAY-LABEL: foo +// VALIST_ARRAY: CompoundStmt +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used argp 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: |-CallExpr +// VALIST_ARRAY: | |-ImplicitCastExpr {{.*}} 'void (*)(struct __va_list_tag *, ...)' +// VALIST_ARRAY: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (struct __va_list_tag *, ...)' +// VALIST_ARRAY: | |-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: | | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_ARRAY: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_ARRAY: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-VAArgExpr {{.*}} 'int *' +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: `-CallExpr +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'void (*)(struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (struct __va_list_tag *)' +// VALIST_ARRAY: `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' + +// VALIST_ARRAY-LABEL: test_va_list_on_fp +// VALIST_ARRAY: |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_ARRAY: |-ParmVarDecl {{.*}} used args 'struct __va_list_tag *' +// VALIST_ARRAY: `-CompoundStmt +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, struct __va_list_tag *)' cinit +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'void (unsigned int, struct __va_list_tag *)' Function {{.*}} 'baz' 'void (unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: `-CallExpr {{.*}} 'void' +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_ARRAY: `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: `-DeclRefExpr {{.*}} 'struct __va_list_tag *' lvalue ParmVar {{.*}} 'args' 'struct __va_list_tag *' + +// VALIST_CHAR_PTR: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used argp 'va_list':'char *' +// VALIST_CHAR_PTR-LABEL: foo +// VALIST_CHAR_PTR: CompoundStmt +// VALIST_CHAR_PTR: |-DeclStmt +// VALIST_CHAR_PTR: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_CHAR_PTR: |-DeclStmt +// VALIST_CHAR_PTR: | `-VarDecl {{.*}} used argp 'va_list':'char *' +// VALIST_CHAR_PTR: |-CallExpr +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_CHAR_PTR: | |-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_CHAR_PTR: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_CHAR_PTR: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-VAArgExpr {{.*}} 'int *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR: `-CallExpr +// VALIST_CHAR_PTR: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_CHAR_PTR: `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used args 'va_list':'char *' +// VALIST_CHAR_PTR: | `-CompoundStmt {{.*}} +// VALIST_CHAR_PTR: | |-DeclStmt +// VALIST_CHAR_PTR: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_CHAR_PTR: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | `-CallExpr {{.*}} 'void' +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'va_list':'char *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue ParmVar {{.*}} 'args' 'va_list':'char *' + +// VALIST_STRUCT: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used argp 'va_list':'struct __va_list' +// VALIST_STRUCT-LABEL: foo +// VALIST_STRUCT: CompoundStmt +// VALIST_STRUCT: |-DeclStmt +// VALIST_STRUCT: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_STRUCT: |-DeclStmt +// VALIST_STRUCT: | `-VarDecl {{.*}} used argp 'va_list':'struct __va_list' +// VALIST_STRUCT: |-CallExpr {{.*}} 'void' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_STRUCT: | |-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_STRUCT: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_STRUCT: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-VAArgExpr {{.*}} 'int *' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' +// VALIST_STRUCT: `-CallExpr {{.*}} 'void' +// VALIST_STRUCT: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_STRUCT: `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' + +// VALIST_STRUCT-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used args 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-CompoundStmt +// VALIST_STRUCT: | |-DeclStmt +// VALIST_STRUCT: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_STRUCT: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_STRUCT: | `-CallExpr {{.*}} 'void' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue ParmVar {{.*}} 'args' 'va_list':'struct __va_list' + +// VALIST_VOID_PTR: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used argp 'va_list':'void *' +// VALIST_VOID_PTR-LABEL: foo +// VALIST_VOID_PTR: CompoundStmt +// VALIST_VOID_PTR: |-DeclStmt +// VALIST_VOID_PTR: | `-VarDecl {{.*}} col:27 used u 'int *__unsafe_indexable' cinit +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_VOID_PTR: |-DeclStmt +// VALIST_VOID_PTR: | `-VarDecl {{.*}} col:10 used argp 'va_list':'void *' +// VALIST_VOID_PTR: |-CallExpr +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_VOID_PTR: | |-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_VOID_PTR: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_VOID_PTR: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-VAArgExpr {{.*}} 'int *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' +// VALIST_VOID_PTR: `-CallExpr {{.*}} 'void' +// VALIST_VOID_PTR: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_VOID_PTR: `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' + +// VALIST_VOID_PTR-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used args 'va_list':'void *' +// VALIST_VOID_PTR: | `-CompoundStmt +// VALIST_VOID_PTR: | |-DeclStmt +// VALIST_VOID_PTR: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_VOID_PTR: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | `-CallExpr {{.*}} 'void' +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'va_list':'void *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue ParmVar {{.*}} 'args' 'va_list':'void *' + +// COMMON-LABEL: bar +// COMMON: CallExpr +// COMMON: |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, ...)' +// COMMON: | `-DeclRefExpr {{.*}} 'void (unsigned int, ...)' Function {{.*}} 'foo' 'void (unsigned int, ...)' +// COMMON: |-ImplicitCastExpr {{.*}} 'unsigned int' +// COMMON: | `-IntegerLiteral {{.*}} 'int' 1 +// COMMON: |-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// COMMON: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// COMMON: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'p' 'int *__bidi_indexable' +// COMMON: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// COMMON: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// COMMON: `-DeclRefExpr {{.*}} 'int[10]' lvalue Var {{.*}} 'arr' 'int[10]' diff --git a/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c b/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c new file mode 100644 index 0000000000000..1da0255a25414 --- /dev/null +++ b/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c @@ -0,0 +1,116 @@ +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +extern float foo[]; +// CHECK:|-VarDecl {{.*}} used foo 'float[]' extern + +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +float *__indexable wide_f[] = {foo}; +// CHECK-NEXT:|-VarDecl {{.*}} wide_f 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'foo' 'float[]' + + +extern float bar[]; +float bar[] = {1, 2, 3, 4}; +float *__indexable wide_f2[] = {bar}; +// CHECK-NEXT:|-VarDecl {{.*}} used bar 'float[]' extern +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used bar 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} wide_f2 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'bar' 'float[4]' + +extern float baz[]; +float baz[] = {1, 2, 3, 4}; +extern float baz[]; +float *__indexable wide_f3[] = {baz}; +// CHECK-NEXT:|-VarDecl {{.*}} used baz 'float[]' extern +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used baz 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used baz 'float[4]' extern +// CHECK-NEXT:|-VarDecl {{.*}} wide_f3 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'baz' 'float[4]' + +// expected-note@+2{{use __attribute__((visibility("hidden"))) attribute instead}} +// expected-warning@+1{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} +__private_extern__ float quz[]; +float quz[] = {1, 2, 3, 4}; +// expected-note@+2{{use __attribute__((visibility("hidden"))) attribute instead}} +// expected-warning@+1{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} +__private_extern__ float quz[]; +float *__indexable wide_f4[] = {quz}; +// CHECK-NEXT:|-VarDecl {{.*}} used quz 'float[]' __private_extern__ +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used quz 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used quz 'float[4]' __private_extern__ +// CHECK-NEXT:|-VarDecl {{.*}} wide_f4 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'quz' 'float[4]' + +// expected-warning@+1{{tentative array definition assumed to have one element}} +static float qux[]; +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +float *__bidi_indexable wide_f5[] = {qux}; +// CHECK-NEXT:|-VarDecl {{.*}} used qux 'float[1]' static +// CHECK-NEXT:|-VarDecl {{.*}} wide_f5 'float *__bidi_indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__bidi_indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'qux' 'float[1]' + +void f(void) { + extern float bar[]; + extern float quxx[]; + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + static float *__bidi_indexable wide_f6[] = {quxx, bar, baz}; +} +// CHECK-LABEL: f 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} parent {{.*}} prev {{.*}} used bar 'float[4]' extern +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} parent {{.*}} used quxx 'float[]' extern +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} wide_f6 'float *__bidi_indexable[3]' static cinit +// CHECK-NEXT: `-InitListExpr {{.*}} 'float *__bidi_indexable[3]' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'quxx' 'float[]' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'bar' 'float[4]' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'baz' 'float[4]' diff --git a/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c b/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c new file mode 100644 index 0000000000000..3ba6f214a68aa --- /dev/null +++ b/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c @@ -0,0 +1,583 @@ + +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__counted_by(len) buf, int len) {} +void Bar(int *__sized_by(siz) buf, int siz) {} +// CHECK: FunctionDecl [[func_Foo:0x[^ ]+]] {{.+}} Foo +// CHECK: FunctionDecl [[func_Bar:0x[^ ]+]] {{.+}} Bar + +void Test(void) { +// CHECK-LABEL: FunctionDecl {{.+}} Test + int value = 0; +// CHECK: VarDecl [[var_value:0x[^ ]+]] {{.+}} value + int *__single p = &value; +// CHECK: VarDecl [[var_p:0x[^ ]+]] {{.+}} p + int *q = &value; +// CHECK: VarDecl [[var_q:0x[^ ]+]] {{.+}} q + S s; +// CHECK: VarDecl [[var_s:0x[^ ]+]] {{.+}} s +Lassign: + s.buf = &value; + s.len = 1; +// CHECK-LABEL: Lassign +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_value]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +Lfoo_sbuf_0: + Foo(s.buf, 0); +// CHECK-LABEL: Lfoo_sbuf_0 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' + +Lfoo_sbuf_1: + Foo(s.buf, 1); +// CHECK-LABEL: Lfoo_sbuf_1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' + +Lfoo_sbuf_5: + Foo(s.buf, 5); +// CHECK-LABEL: Lfoo_sbuf_5 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' + +Lfoo_p_0: + Foo(p, 0); +// CHECK-LABEL: Lfoo_p_0 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' + +Lfoo_p_1: + Foo(p, 1); +// CHECK-LABEL: Lfoo_p_1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' + +Lfoo_q_5: + Foo(q, 5); +// CHECK-LABEL: Lfoo_q_5 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_21]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_q]] +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' + +Lbar_sbuf_slen: + Bar(s.buf, s.len * sizeof(int)); +// CHECK-LABEL: Lbar_sbuf_slen +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(siz), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Bar]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(siz)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' + +Lbar_sbuf_11: + Bar(s.buf, 11 * sizeof(int)); +// CHECK-LABEL: Lbar_sbuf_11 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(siz), int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_Bar]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(siz)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_30]] +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_31]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-IntegerLiteral {{.+}} 11 +// CHECK: | | `-UnaryExprOrTypeTraitExpr +// CHECK: | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +} + +// CHECK-LABEL: TestError +void TestError(void) { + int value = 0; + int *__single p = &value; + // expected-error@+1{{passing 'int *__single' to parameter 'buf' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 5 always fails}} + Foo(p, 5); +} diff --git a/clang/test/BoundsSafety/Analysis/analyzer_test.py b/clang/test/BoundsSafety/Analysis/analyzer_test.py new file mode 100644 index 0000000000000..03124333fe7bf --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/analyzer_test.py @@ -0,0 +1,48 @@ +import lit.formats +import lit.TestRunner + +# Custom format class for static analyzer tests +class AnalyzerTest(lit.formats.ShTest): + + def __init__(self, execute_external, use_z3_solver=False): + super(AnalyzerTest, self).__init__(execute_external) + self.use_z3_solver = use_z3_solver + + def execute(self, test, litConfig): + results = [] + + # Parse any test requirements ('REQUIRES: ') + saved_test = test + lit.TestRunner.parseIntegratedTestScript(test) + + if 'z3' not in test.requires: + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=range')) + + if results[-1].code == lit.Test.FAIL: + return results[-1] + + # If z3 backend available, add an additional run line for it + if self.use_z3_solver == '1': + assert(test.config.clang_staticanalyzer_z3 == '1') + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=z3 -DANALYZER_CM_Z3')) + + # Combine all result outputs into the last element + for x in results: + if x != results[-1]: + results[-1].output = x.output + results[-1].output + + if results: + return results[-1] + return lit.Test.Result(lit.Test.UNSUPPORTED, + "Test requires the following unavailable features: z3") + + def executeWithAnalyzeSubstitution(self, test, litConfig, substitution): + saved_substitutions = list(test.config.substitutions) + test.config.substitutions.append(('%analyze', substitution)) + result = lit.TestRunner.executeShTest(test, litConfig, + self.execute_external) + test.config.substitutions = saved_substitutions + + return result diff --git a/clang/test/BoundsSafety/Analysis/cfg.c b/clang/test/BoundsSafety/Analysis/cfg.c new file mode 100644 index 0000000000000..b434cfa8b3533 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/cfg.c @@ -0,0 +1,182 @@ + +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck %s + +#include + +// BoundsSafetyPointerPromotionExpr + +// CHECK-LABEL: int *__bidi_indexablepromote_counted_by(int *ptr, unsigned int len) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: ptr +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int *__single __counted_by(len)) +// CHECK-NEXT: 3: len +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, unsigned int) +// CHECK-NEXT: 5: [B1.2] (BoundsSafetyPointerPromotionExpr, int *__bidi_indexable) +// CHECK-NEXT: 6: return [B1.5]; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +int *__bidi_indexable +promote_counted_by(int *__counted_by(len) ptr, unsigned len) { + return ptr; +} + +// BoundsCheckExpr + +typedef struct { + unsigned long long size; + void *__sized_by(size) buf; +} counted_buf; + +// CHECK-LABEL: void bounds_check(counted_buf *bf, void *__bidi_indexablebuf, unsigned long long size) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: buf +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, void *__bidi_indexable) +// CHECK-NEXT: 3: size +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, unsigned long long) +// CHECK-NEXT: 5: [B1.2] (ImplicitCastExpr, BoundsSafetyPointerCast, void *__single __sized_by(size)) +// CHECK-NEXT: 6: bf +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 8: [B1.7]->buf +// CHECK-NEXT: 9: [B1.8] = [B1.5] +// CHECK-NEXT: 10: bf +// CHECK-NEXT: 11: [B1.10] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 12: [B1.11]->size +// CHECK-NEXT: 13: [B1.12] = [B1.4] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void bounds_check(counted_buf *bf, + void *__bidi_indexable buf, + unsigned long long size) { + bf->buf = buf; + bf->size = size; +} + +// Combined + +void *__sized_by(size) my_alloc(unsigned long long size); + +// CHECK-LABEL: int promote_and_bounds_check_in_middle(int v, counted_buf *bf) +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: v +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: return [B1.2]; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: 9 +// CHECK-NEXT: 2: return [B2.1]; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: 10 +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, IntegralCast, unsigned long long) +// CHECK-NEXT: 3: my_alloc +// CHECK-NEXT: 4: [B3.3] (ImplicitCastExpr, FunctionToPointerDecay, void *__single __sized_by(size)(*__single)(unsigned long long)) +// CHECK-NEXT: 5: [B3.4]([B3.2]) +// CHECK-NEXT: 6: [B3.5] (BoundsSafetyPointerPromotionExpr, void *__bidi_indexable) +// CHECK-NEXT: 7: 8 +// CHECK-NEXT: 8: [B3.7] (ImplicitCastExpr, IntegralCast, unsigned long long) +// CHECK-NEXT: 9: [B3.6] (ImplicitCastExpr, BoundsSafetyPointerCast, void *__single __sized_by(size)) +// CHECK-NEXT: 10: bf +// CHECK-NEXT: 11: [B3.10] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 12: [B3.11]->buf +// CHECK-NEXT: 13: [B3.12] = [B3.9] +// CHECK-NEXT: 14: bf +// CHECK-NEXT: 15: [B3.14] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 16: [B3.15]->size +// CHECK-NEXT: 17: [B3.16] = [B3.8] +// CHECK-NEXT: 18: v +// CHECK-NEXT: 19: [B3.18] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 20: 9 +// CHECK-NEXT: 21: [B3.19] < [B3.20] +// CHECK-NEXT: T: if [B3.21] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B4] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: return [B4.1]; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: v +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: ![B5.2] +// CHECK-NEXT: T: if [B5.3] +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (3): B1 B2 B4 +int promote_and_bounds_check_in_middle(int v, counted_buf *bf) { + if (!v) + return 0; + + bf->buf = my_alloc(10); + bf->size = 8; + + if (v < 9) + return 9; + + return v; +} + + +// example from Sema/unreachable-noret.c + +#define NO_RETURN __attribute__((noreturn)) +void NO_RETURN halt(const void * const p_fatal_error); + +// CHECK-LABEL: static void handler_private(const void *p_stack) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1 (NORETURN)] +// CHECK-NEXT: 1: int foo; +// CHECK-NEXT: 2: halt +// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*__single)(const void *__singleconst) __attribute__((noreturn))) +// CHECK-NEXT: 4: foo +// CHECK-NEXT: 5: &[B1.4] +// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, BitCast, const void *__bidi_indexable) +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, BoundsSafetyPointerCast, const void *__single) +// CHECK-NEXT: 8: [B1.3]([B1.7]) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +static void NO_RETURN handler_private(const void *__sized_by(0x78) p_stack) +{ + int foo; + halt(&foo); +} + +// CHECK-LABEL: void handler_irq(const void *p_stack) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1 (NORETURN)] +// CHECK-NEXT: 1: p_stack +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, const void *__single __sized_by(120)) +// CHECK-NEXT: 3: 120 +// CHECK-NEXT: 4: [B1.2] (BoundsSafetyPointerPromotionExpr, const void *__bidi_indexable) +// CHECK-NEXT: 5: 120 +// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, IntegralCast, long) +// CHECK-NEXT: 7: handler_private +// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, FunctionToPointerDecay, void (*__single)(const void *__single __sized_by(120)) __attribute__((noreturn))) +// CHECK-NEXT: 9: [B1.4] (ImplicitCastExpr, BoundsSafetyPointerCast, const void *__single __sized_by(120)) +// CHECK-NEXT: 10: [B1.8]([B1.9]) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void NO_RETURN handler_irq(const void *__sized_by(0x78) p_stack) +{ + handler_private(p_stack); +} diff --git a/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c new file mode 100644 index 0000000000000..92557c8ded515 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c @@ -0,0 +1,10 @@ + +// RUN: %clang_cc1 -analyze -analyzer-checker=deadcode.DeadStores -fbounds-safety -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=deadcode.DeadStores -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +int foo() { + int arr[10]; + int a = 10; + // expected-warning@+1{{Value stored to 'ptr' during its initialization is never read [deadcode.DeadStores]}} + int *ptr = __builtin_unsafe_forge_bidi_indexable(arr, 4 * sizeof(int)); + return 0; +} diff --git a/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c new file mode 100644 index 0000000000000..8c07b9a7c98b8 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c @@ -0,0 +1,12 @@ + +// RUN: %clang_cc1 -analyze -analyzer-checker=core.uninitialized.UndefReturn -fbounds-safety -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core.uninitialized.UndefReturn -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// XFAIL: * +// rdar://72163355 +int foo() { + int arr[10]; + int a = 10; + int *ptr = __builtin_unsafe_forge_bidi_indexable(arr, 4 * sizeof(int)); + // expected-warning@+1{{Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]}} + return arr[a]; +} diff --git a/clang/test/BoundsSafety/Analysis/lit.local.cfg b/clang/test/BoundsSafety/Analysis/lit.local.cfg new file mode 100644 index 0000000000000..1e8cf4c3b7c4b --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/lit.local.cfg @@ -0,0 +1,28 @@ +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: +from lit.llvm.subst import ToolSubst +import site + +# Load the custom analyzer test format, which runs the test again with Z3 if it +# is available. +site.addsitedir(os.path.dirname(__file__)) +import analyzer_test +config.test_format = analyzer_test.AnalyzerTest( + config.test_format.execute_external, config.use_z3_solver) + +# Filtering command used by Clang Analyzer tests (when comparing .plist files +# with reference output) +config.substitutions.append(('%normalize_plist', + "grep -Ev '%s|%s|%s'" % + ('^[[:space:]]*.* version .*[[:space:]]*$', + '^[[:space:]]*/.*[[:space:]]*$', + '^[[:space:]]*.:.*[[:space:]]*$'))) + +# Filtering command for testing SARIF output against reference output. +config.substitutions.append(('%normalize_sarif', + "grep -Ev '^[[:space:]]*(%s|%s|%s)[[:space:]]*$'" % + ('"uri": "file:.*%basename_t"', + '"version": ".* version .*"', + '"version": "2.1.0"'))) + +if not config.root.clang_staticanalyzer: + config.unsupported = True diff --git a/clang/test/BoundsSafety/Analysis/pointer-cast.c b/clang/test/BoundsSafety/Analysis/pointer-cast.c new file mode 100644 index 0000000000000..9bb3469642a94 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/pointer-cast.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -analyze -fbounds-safety -w -verify -analyzer-checker=core %s +// RUN: %clang_cc1 -analyze -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -w -verify -analyzer-checker=core %s + +// expected-no-diagnostics + +#include + +void test_void_pointer(void *__bidi_indexable p) { + void *q = p; + if (q) {} + q != 0; // no-crash +} + +void test_opaque_pointer(struct S *__bidi_indexable p) { + struct S *q = p; + if (!q) { + q != 0; // no-crash + } +} diff --git a/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c b/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c new file mode 100644 index 0000000000000..58c58771c897b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c @@ -0,0 +1,77 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefix ACCESS-SIZE %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefix ACCESS-SIZE %s +#include +int get_int(void*); +typedef int fn_t(void *); + +void receive(fn_t); + +// The codegen with and without `access_size` should be identical + +// ACCESS-SIZE-LABEL: define dso_local void @borked( +// ACCESS-SIZE-SAME: ) #[[ATTR0:[0-9]+]] { +// ACCESS-SIZE-NEXT: [[ENTRY:.*:]] +// ACCESS-SIZE-NEXT: [[F:%.*]] = alloca ptr, align 8 +// ACCESS-SIZE-NEXT: [[W:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// ACCESS-SIZE-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// ACCESS-SIZE-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// ACCESS-SIZE-NEXT: store ptr @get_int, ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[W]], i8 0, i64 24, i1 false) +// ACCESS-SIZE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[W]], i64 24, i1 false) +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// ACCESS-SIZE-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// ACCESS-SIZE-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// ACCESS-SIZE-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR3]], null, !annotation [[META2:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT9:.*]], !annotation [[META2]] +// ACCESS-SIZE: [[BOUNDSCHECK_NOTNULL]]: +// ACCESS-SIZE-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], !annotation [[META3:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META3]] +// ACCESS-SIZE: [[TRAP]]: +// ACCESS-SIZE-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META3]] +// ACCESS-SIZE-NEXT: unreachable, !annotation [[META3]] +// ACCESS-SIZE: [[CONT]]: +// ACCESS-SIZE-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB7]], !annotation [[META4:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP5]], label %[[CONT9]], label %[[TRAP8:.*]], !annotation [[META4]] +// ACCESS-SIZE: [[TRAP8]]: +// ACCESS-SIZE-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// ACCESS-SIZE-NEXT: unreachable, !annotation [[META4]] +// ACCESS-SIZE: [[CONT9]]: +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: [[TMP6:%.*]] = load ptr, ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: call void @receive(ptr noundef [[TMP6]]) +// ACCESS-SIZE-NEXT: ret void +// +void borked(void) +{ + fn_t *f = get_int; // This is implicitly __single, not __bidi_indexable + void *w = 0; // Implicitly __bidi_indexable + // Bounds check here + // Converting + // int (*__bidi_indexable)(void *__single) -> rn_matchf_t *__single + // + // requires checking that the wide pointer is in bounds. + f = w; + receive(f); +} +//. +// ACCESS-SIZE: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// ACCESS-SIZE: [[META3]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// ACCESS-SIZE: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c new file mode 100644 index 0000000000000..0a0fda422e5de --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int arr[10]; + +int v; + +int main() { + int *ptr = &arr[v]; + return *ptr; +} + +// CHECK-LABEL: @v +// CHECK-LABEL: @arr +// CHECK-LABEL: @main( +// CHECK: entry: +// CHECK: [[TMP0:%.*]] = load i32, ptr @v, align 4, {{!tbaa ![0-9]+}} +// CHECK: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr @arr, i64 [[IDXPROM]] +// CHECK: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], getelementptr inbounds (i8, ptr @arr, i64 40), {{!annotation ![0-9]+}} +// CHECK: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], @arr, {{!annotation ![0-9]+}} +// CHECK: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont1: +// CHECK: [[TMP3:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK: ret i32 [[TMP3]] +// diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c new file mode 100644 index 0000000000000..425917fb18f8e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int arr[10]; + +int v; + +// CHECK-LABEL: @main( +// CHECK: entry: +// CHECK: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: store i32 0, ptr [[RETVAL]], align 4 +// CHECK: [[TMP0:%.*]] = load i32, ptr @v, align 4 +// CHECK: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK: store ptr @arr, ptr [[TMP1]], align 8 +// CHECK: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK: store ptr getelementptr inbounds (i32, ptr @arr, i64 10), ptr [[TMP2]], align 8 +// CHECK: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK: store ptr @arr, ptr [[TMP3]], align 8 +// CHECK: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont: +// CHECK: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont2: +// CHECK: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK: ret i32 [[TMP15]] +// +int main() { + int *ptr = &arr[v]; + return *ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c b/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c new file mode 100644 index 0000000000000..c34c6044fedb0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c @@ -0,0 +1,21 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +struct __attribute__((packed)) foo { + char x; // field 0 + short s; // field 1 + // : field 2 + unsigned __attribute__((aligned(4))) u; // field 3 +}; + +// CHECK-LABEL: @p( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[S:%.*]], i64 4 +// CHECK-NEXT: ret ptr [[TMP0]] +// +unsigned *p(struct foo *s) { + return &s->u; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c b/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c new file mode 100644 index 0000000000000..ee83002e032b1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +struct S { + int i; + char *p; + long l; +}; + +// CHECK-LABEL: @fails_oob +// CHECK: tail call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !annotation ![[ANNOTATION:[0-9]+]] +// CHECK-NEXT: unreachable, !annotation ![[ANNOTATION]] +int fails_oob() { + struct S s = {1, 0, 2}; + struct S *ps = &s; + ps = &ps->i; + ps = &s.i; + return ps->i; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c b/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c new file mode 100644 index 0000000000000..2e9b32a4e4656 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +union U { + int i; + long l; +}; + +// CHECK-LABEL: @foo +// CHECK: ret i32 1 +int foo(void) { + union U u; + int *i = &u.i; + long *l = &u.l; + return (void *)i == (void *)l; +} diff --git a/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c b/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c new file mode 100644 index 0000000000000..098f394901b43 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c @@ -0,0 +1,193 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64e-apple-iphoneos %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64e-apple-iphoneos %s -o - | FileCheck %s + +#include + +struct foo { + char *ptr; + int i; + long l; +}; + +// CHECK-LABEL: @addrof_single_ptr_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[F:%.*]] +// +char **addrof_single_ptr_to_single(struct foo *f) { + return &f->ptr; +} + +// CHECK-LABEL: @addrof_single_i_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F:%.*]], i64 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *addrof_single_i_to_single(struct foo *f) { + return &f->i; +} + +// CHECK-LABEL: @addrof_single_l_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F:%.*]], i64 16 +// CHECK-NEXT: ret ptr [[TMP0]] +// +long *addrof_single_l_to_single(struct foo *f) { + return &f->l; +} + +static inline char **__addrof_bidi_ptr_to_single(struct foo *__bidi_indexable f) { + return &f->ptr; +} + +// CHECK-LABEL: @addrof_bidi_ptr_to_single( +// CHECK-NEXT: __addrof_bidi_ptr_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: ret ptr [[F]] +// +char **addrof_bidi_ptr_to_single(void) { + struct foo f; + return __addrof_bidi_ptr_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_ptr_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +char **addrof_bidi_ptr_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f + 1; + return __addrof_bidi_ptr_to_single(fp); +} + + +// CHECK-LABEL: @addrof_bidi_ptr_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[F]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_PTR_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_ptr_to_single.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[BOUND_PTR_ARITH]] +// +char **addrof_bidi_ptr_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f - 1; + return __addrof_bidi_ptr_to_single(fp); +} + +static inline int *__addrof_bidi_i_to_single(struct foo *__bidi_indexable f) { + return &f->i; +} + +// CHECK-LABEL: @addrof_bidi_i_to_single( +// CHECK-NEXT: __addrof_bidi_i_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 8 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *addrof_bidi_i_to_single(void) { + struct foo f; + return __addrof_bidi_i_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_i_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *addrof_bidi_i_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f+1; + return __addrof_bidi_i_to_single(fp); +} + +// CHECK-LABEL: @addrof_bidi_i_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[F]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_I_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_i_to_single.exit: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[F]], i64 -16 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP3]] +// +int *addrof_bidi_i_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f-1; + return __addrof_bidi_i_to_single(fp); +} + +static inline long *__addrof_bidi_l_to_single(struct foo *__bidi_indexable f) { + return &f->l; +} + +// CHECK-LABEL: @addrof_bidi_l_to_single( +// CHECK-NEXT: __addrof_bidi_l_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 16 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +long *addrof_bidi_l_to_single(void) { + struct foo f; + return __addrof_bidi_l_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_l_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +long *addrof_bidi_l_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f+1; + return __addrof_bidi_l_to_single(fp); +} + +// CHECK-LABEL: @addrof_bidi_l_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[F]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_L_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_l_to_single.exit: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[F]], i64 -8 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP3]] +// +long *addrof_bidi_l_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f-1; + return __addrof_bidi_l_to_single(fp); +} diff --git a/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c new file mode 100644 index 0000000000000..be9c3578b50c8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c @@ -0,0 +1,88 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -emit-llvm -triple arm64e -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s + +#include +#include "mock-header.h" + + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 10, ptr [[CNT]], align 4 +// CHECK-NEXT: store i32 4, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[CNT]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_calloc(i32 noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[TMP0]], [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[CALL]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[MUL]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[CNT]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP12]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[TMP13]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[TMP13]], [[WIDE_PTR_LB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP13]], align 4 +// CHECK-NEXT: ret i32 [[TMP16]] +// +int foo() { + int cnt = 10; + int siz = sizeof(int); + int *ptr = my_calloc(cnt, siz); + return ptr[cnt-1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h new file mode 100644 index 0000000000000..a4e57d475c428 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h @@ -0,0 +1,7 @@ +#ifndef MOCK_HEADER_H +#define MOCK_HEADER_H + +#pragma clang system_header +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); + +#endif /* MOCK_HEADER_H */ diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c new file mode 100644 index 0000000000000..808447d01c03d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c @@ -0,0 +1,188 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: @count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void count_ok(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + buf = arr; + len = 12; +} + +// CHECK-LABEL: @negative_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void negative_count_trap(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + int n = 0; + len = n; + buf = arr; +} + +// CHECK-LABEL: @too_big_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void too_big_count_trap(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + int n = 13; + buf = arr; + len = n; +} + +// CHECK-LABEL: @overflow_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_trap(int *__counted_by(len - 2) buf, size_t len) { + int arr[10]; + int n = 0; + buf = arr; + len = n; +} + +// CHECK-LABEL: @local_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void local_count_ok(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @local_count_ok_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void local_count_ok_2(void) { + int arr[10]; + int len = 2; + int *__counted_by(len - 2) buf; + len = 12; + buf = arr; +} + +// CHECK-LABEL: @local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void local_count_trap(void) { + int arr[10]; + int n = 0; + int len = n; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @loob_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP16_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void loob_local_count_trap(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr - 1; +} + +// CHECK-LABEL: @uoob_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void uoob_local_count_trap(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr + 1; +} + +// CHECK-LABEL: @too_big_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void too_big_local_count_trap(void) { + int arr[10]; + int n = 13; + int len = 2; + int *__counted_by(len - 2) buf; + buf = arr; + len = n; +} + +// CHECK-LABEL: @overflow_local_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_local_unsigned_count_trap(void) { + int arr[10]; + int n = 0; + int len = n; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void overflow_unsigned_count_size_ok(void *__sized_by(count * size) buf, size_t count, size_t size) { + int arr[10]; + buf = arr; + count = __SIZE_MAX__; + size = __SIZE_MAX__; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_trap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_size_trap2(void *__sized_by(count * size) buf, size_t count, size_t size) { + int arr[10]; + buf = arr; + count = __SIZE_MAX__; + size = __SIZE_MAX__; + + char *p = buf; + p[1] = 0; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_index_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[SIZE:%.*]], [[COUNT:%.*]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[MUL]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_size_index_trap(int *__counted_by(count * size) buf, size_t count, size_t size) { + buf = buf; + count = (size_t)(__SIZE_MAX__ >> 2) + (size_t)1; + size = 1; + + buf[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c new file mode 100644 index 0000000000000..816b0630105c9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c @@ -0,0 +1,226 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: @count_size_mul( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[BUF:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[C:%.*]], ptr [[C_ADDR]], align 8 +// CHECK-NEXT: store i64 [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i64 0, ptr [[SIZE]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[C_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule i64 [[MUL]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i64 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[SIZE]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void count_size_mul(void *__bidi_indexable ptr, size_t c, size_t s) { + size_t count; + size_t size; + void *__sized_by(count * size) buf; + + count = c; + size = s; + buf = ptr; +} + +// CHECK-LABEL: @iptr_count_size_mul( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[BUF:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[C:%.*]], ptr [[C_ADDR]], align 8 +// CHECK-NEXT: store i64 [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i64 0, ptr [[SIZE]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[C_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_UB11]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_LB14]], [[WIDE_PTR_PTR17]] +// CHECK-NEXT: br i1 [[CMP22]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule i64 [[MUL]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i64 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[SIZE]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void iptr_count_size_mul(void *__bidi_indexable ptr, size_t c, size_t s) { + size_t count; + size_t size; + int *__counted_by(count * size) buf; + + count = c; + size = s; + buf = ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c new file mode 100644 index 0000000000000..54998806065b8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c @@ -0,0 +1,123 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +inline void param_with_count(int *__counted_by(len - 2) buf, int len) {} + +// CHECK-LABEL: @count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void count_ok(void) { + int arr[10]; + param_with_count(arr, 12); +} + +// CHECK-LABEL: @negative_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void negative_count_trap(void) { + int arr[10]; + int len = 0; + param_with_count(arr, len); +} + +// CHECK-LABEL: @too_big_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void too_big_count_trap(void) { + int arr[10]; + int n = 13; + param_with_count(arr, n); +} + +inline void param_with_unsigned_count(int *__counted_by(len - 2) buf, size_t len) {} + +// CHECK-LABEL: @overflow_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void overflow_unsigned_count_trap(void) { + int arr[10]; + int n = 0; + param_with_unsigned_count(arr, n); +} + +void *__sized_by(count * size) return_buf(size_t count, size_t size); + +// CHECK-LABEL: @returned_buf_ok( +// CHECK-NEXT: cont8: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 4, i64 noundef 5) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL]], i64 19 +// CHECK-NEXT: store i8 0, ptr [[TMP0]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void returned_buf_ok(void) { + char *p = return_buf(4, 5); + p[19] = 0; +} + +// CHECK-LABEL: @oob_returned_buf_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 4, i64 noundef 5) #[[ATTR6]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void oob_returned_buf_trap(void) { + char *p = return_buf(4, 5); + p[20] = 0; +} + +// CHECK-LABEL: @overflow_returned_buf_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef -1) #[[ATTR6]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void overflow_returned_buf_trap(void) { + char *p = return_buf(__SIZE_MAX__, __SIZE_MAX__); + p[1] = 0; +} + +// CHECK-LABEL: @overflow_returned_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef -1) #[[ATTR6]] +// CHECK-NEXT: store i8 0, ptr [[CALL]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void overflow_returned_buf_ok(void) { + char *p = return_buf(__SIZE_MAX__, __SIZE_MAX__); + p[0] = 0; +} + +// CHECK-LABEL: @too_big_returned_buf_undef( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef 1) #[[ATTR6]] +// CHECK-NEXT: unreachable +// +void too_big_returned_buf_undef(void) { + char *p = return_buf(__SIZE_MAX__, 1); + p[0] = 0; +} + +// XXX: In this case, the count argument already violates the assumption that the offset doesn't overflow in a signed sense. +// This should actually be caught already when the count is assigned in side the callee, but if the callee's definition is +// not -fbounds-safety'ed, there's not much we can do here. +// CHECK-LABEL: @index_overflow_returned_buf_fixme( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 9223372036854775807, i64 noundef 1) #[[ATTR6]] +// CHECK-NEXT: store i8 0, ptr [[CALL]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void index_overflow_returned_buf_fixme(void) { + char *p = return_buf(__SIZE_MAX__ >> 1, 1); + p[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..c3c57e7848241 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,184 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +inline void param_with_count(int *__counted_by(len - 2) buf, int len) {} + +// CHECK-LABEL: @pass_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 2 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END51:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS:%.*]], label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR41]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP46:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP46]], label [[LAND_RHS48:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs48: +// CHECK-NEXT: [[CMP49:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP49]], [[LAND_RHS48]] ] +// CHECK-NEXT: br label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.end51: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @param_with_count(ptr noundef [[WIDE_PTR_PTR54]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void pass_count(int *__bidi_indexable buf, int len) { + param_with_count(buf, len); +} + +void *__sized_by(count * size) return_buf(size_t count, size_t size); + +// CHECK-LABEL: @get_returned_buf( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i64 [[COUNT:%.*]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @return_buf(i64 noundef [[TMP0]], i64 noundef [[TMP1]]) +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[MUL]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[MUL]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR3]], i64 1 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[TMP8]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[TMP8]], [[WIDE_PTR_LB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: store i8 0, ptr [[TMP8]], align 1 +// CHECK-NEXT: ret void +// +void get_returned_buf(size_t count, size_t size) { + char *p = return_buf(count, size); + p[1] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c b/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c new file mode 100644 index 0000000000000..e0fe4f8166644 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c @@ -0,0 +1,152 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +// CHECK-O0-LABEL: @foo( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[TMP0:%.*]] = getelementptr [10 x i32], ptr [[ARR]], i64 1 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP3]], align 8 +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[WIDE_PTR_PTR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-O0: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP7]], align 8 +// CHECK-O0: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 10 +// CHECK-O0: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2: {{.*}}: +// CHECK-O2: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2: unreachable +// +int foo() { + int arr[10]; + int *ptr = *&arr; + return ptr[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[TMP0:%.*]] = getelementptr [10 x i32], ptr [[ARR]], i64 1 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP3]], align 8 +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[WIDE_PTR_PTR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-O0: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP7]], align 8 +// CHECK-O0: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 9 +// CHECK-O0: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2: {{.*}}: +// CHECK-O2: ret i32 undef +// +int bar() { + int arr[10]; + int *ptr = *&arr; + return ptr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/array-decay.c b/clang/test/BoundsSafety/CodeGen/array-decay.c new file mode 100644 index 0000000000000..8e61581c39175 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array-decay.c @@ -0,0 +1,100 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +// CHECK-O0-LABEL: @foo( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: call void @llvm.memset.p0.i64(ptr align 4 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2: {{.*}}: +// CHECK-O2: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2: unreachable +// +int foo() { + int arr[10] = {0}; + int *ptr = arr; + return ptr[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: call void @llvm.memset.p0.i64(ptr align 4 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2: entry: +// CHECK-O2: ret i32 0 +// +int bar() { + int arr[10] = {0}; + int *ptr = arr; + return ptr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c new file mode 100644 index 0000000000000..0fe60dc6a404e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int main(int argc, char **argv) { + int *__single foo__single[3]; +// CHECK: [[TMP0:%.*]] = alloca [3 x ptr] +// CHECK: call void @llvm.memset.{{.*}}({{.*}} [[TMP0]], {{.*}} 0, {{.*}} 24{{.*}}){{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// ... +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c new file mode 100644 index 0000000000000..293f434861af0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int main(int argc, char **argv) { + int len; + int *__single __counted_by(len) foo__single__counted_by; + +// CHECK: store i32 0, ptr {{%.*}}, align 4, !annotation ![[ANNOT_ZEROINIT:[0-9]+]] +// ... +// CHECK: ![[ANNOT_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c new file mode 100644 index 0000000000000..94f95d1571ad9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__single __counted_by(len) foo__single__counted_by; +}; + +int main(int argc, char **argv) { + struct Foo f; +// CHECK: [[LEN:%.*]] = getelementptr inbounds %struct.Foo, ptr [[STRUCT_F:%.*]], i32 0, i32 0 +// CHECK: store i32 0, ptr [[LEN]], align 8, !annotation ![[ANNOT_ZEROINIT:[0-9]+]] +// CHECK: [[FOO_SINGLE_COUNTED_BY:%.*]] = getelementptr inbounds %struct.Foo, ptr [[STRUCT_F]], i32 0, i32 1 +// CHECK: store ptr null, ptr [[FOO_SINGLE_COUNTED_BY]], align 8, !annotation ![[ANNOT_ZEROINIT]] +// ... +// CHECK: ![[ANNOT_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c new file mode 100644 index 0000000000000..f3b6217840839 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -triple arm64-apple-darwin -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -O0 %s -o - | FileCheck %s + +#include + +char a; +int *__bidi_indexable ptr = &a; +// CHECK: .globl _ptr {{.*}} @ptr +// ... +// CHECK: _ptr: +// CHECK-NEXT: .quad _a +// CHECK-NEXT: .quad _a+1 +// CHECK-NEXT: .quad _a diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c new file mode 100644 index 0000000000000..ad1e8c91011a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array1[1]; +int array10[10]; + +int *__bidi_indexable bidi_ptr10 = array10; +// CHECK: .globl _bidi_ptr10 {{.*}} @bidi_ptr10 +// ... +// CHECK:_bidi_ptr10: +// CHECK-NEXT: .quad _array10 +// CHECK-NEXT: .quad _array10+40 +// CHECK-NEXT: .quad _array10 + +int *__bidi_indexable bidi_ptr1 = array1; +// CHECK: .globl _bidi_ptr1 {{.*}} @bidi_ptr1 +// ... +// CHECK:_bidi_ptr1: +// CHECK-NEXT: .quad _array1 +// CHECK-NEXT: .quad _array1 +// CHECK-NEXT: .quad _array1 + +int *__bidi_indexable bidi_ptr10_offset3 = array10 + 3; +// CHECK: .globl _bidi_ptr10_offset3 {{.*}} @bidi_ptr10_offset3 +// ... +// CHECK: _bidi_ptr10_offset3: +// CHECK-NEXT: .quad _array10+12 +// CHECK-NEXT: .quad _array10+40 +// CHECK-NEXT: .quad _array10 + +int array_pp11_7[11][7]; +int *__bidi_indexable bidi_ptrpp11_7_offset3 = array_pp11_7[3]; +// CHECK: .globl _bidi_ptrpp11_7_offset3 {{.*}} @bidi_ptrpp11_7_offset3 +// ... +// CHECK: _bidi_ptrpp11_7_offset3: +// CHECK-NEXT: .quad _array_pp11_7+84 +// CHECK-NEXT: .quad _array_pp11_7+308 +// CHECK-NEXT: .quad _array_pp11_7 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c new file mode 100644 index 0000000000000..c6cd37512c9d4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array10[10]; + +int *__attribute__((bidi_indexable)) bidi0 = array10 + 10; + +// CHECK: _bidi0: +// CHECK: .quad _array10+40 +// CHECK: .quad _array10+40 +// CHECK: .quad _array10 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c new file mode 100644 index 0000000000000..7310e621af109 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array0[0]; +int *__attribute__((bidi_indexable)) bidi_ptr0 = array0; + +// CHECK: _bidi_ptr0: +// CHECK: .quad _array0 +// CHECK: .quad _array0 +// CHECK: .quad _array0 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c new file mode 100644 index 0000000000000..81d90eb48081a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int *__single foo__single; +int *__indexable foo__indexable; +int *__bidi_indexable foo__bidi_indexable; + +// CHECK: @foo__single = global ptr null +// CHECK: @foo__indexable = global %"__bounds_safety::wide_ptr.indexable" zeroinitializer +// CHECK: @foo__bidi_indexable = global %"__bounds_safety::wide_ptr.bidi_indexable" zeroinitializer + +int main(int argc, char **argv) { } diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c new file mode 100644 index 0000000000000..e24644fb95d53 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +void single(void) { + // CHECK: [[TMP0:%.*]] = alloca ptr, align 8 + // CHECK: store ptr null, ptr [[TMP0:%.*]], align 8, !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] + int *__single foo__single; +} + +void indexable(void) { + // CHECK: [[TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 + // CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[TMP1]], i8 0, i64 16, i1 false), !annotation ![[ANNOT_ZERO_INIT]] + int *__indexable foo__indexable; +} + +void bidi_indexable(void) { + // CHECK: [[TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 + // CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[TMP2]], i8 0, i64 24, i1 false), !annotation ![[ANNOT_ZERO_INIT]] + int *__bidi_indexable foo__bidi_indexable; +} + +void counted_by(void) { + // CHECK: [[TMP3:%.*]] = alloca ptr, align 8 + // CHECK: store ptr null, ptr [[TMP3:%.*]], align 8, !annotation ![[ANNOT_ZERO_INIT]] + int len; + int *__single __counted_by(len) foo__single__counted_by; +} + +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c new file mode 100644 index 0000000000000..d62aa629e12dd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +struct Foo { + int *__single foo__single; + int *__indexable foo__indexable; + int *__bidi_indexable foo__bidi_indexable; + int len; + int *__single __counted_by(len) foo__single__counted_by; +}; + +int main(int argc, char **argv) { + struct Foo f; +// CHECK: [[FOO_SINGLE:%.*]] = getelementptr inbounds %struct.Foo, ptr [[FOO_F:%.*]], i32 0, i32 0 +// CHECK: store ptr null, ptr [[FOO_SINGLE]]{{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// CHECK: [[FOO_INDEXABLE:%.*]] = getelementptr inbounds %struct.Foo, ptr [[FOO_F]], i32 0, i32 1 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[FOO_INDEXABLE]], i8 0, i64 16, i1 false){{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// CHECK: [[FOO_BIDI_INDEX:%.*]] = getelementptr inbounds %struct.Foo, ptr [[FOO_F]], i32 0, i32 2 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[FOO_BIDI_INDEX]], i8 0, i64 24, i1 false){{.*}} !annotation ![[ANNOT_ZERO_INIT]] +// CHECK: getelementptr inbounds %struct.Foo, ptr [[FOO_F]], i32 0, i32 3 +// CHECK: [[FOO_SINGLE_COUNTED_BY:%.*]] = getelementptr inbounds %struct.Foo, ptr [[FOO_F]], i32 0, i32 4 +// CHECK: store ptr null, ptr [[FOO_SINGLE_COUNTED_BY]], align 8{{.*}} !annotation ![[ANNOT_ZERO_INIT]] +// ... +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c new file mode 100644 index 0000000000000..1bb6cdacd3845 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c @@ -0,0 +1,46 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +union Foo1 { + int *__single foo__single; + int b; +}; + +union Foo2 { + int a; + int *__indexable foo__indexable; + int b; +}; + +union Foo3 { + int *__single a; + int *__single b; + int *__single c; +}; + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARGC_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARGV_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[F1:%.*]] = alloca [[UNION_FOO1:%.*]], align 8 +// CHECK-NEXT: [[F2:%.*]] = alloca [[UNION_FOO2:%.*]], align 8 +// CHECK-NEXT: [[F3:%.*]] = alloca [[UNION_FOO3:%.*]], align 8 +// CHECK-NEXT: store i32 [[ARGC:%.*]], ptr [[ARGC_ADDR]], align 4 +// CHECK-NEXT: store ptr [[ARGV:%.*]], ptr [[ARGV_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F1]], i8 0, i64 8, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F2]], i8 0, i64 16, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F3]], i8 0, i64 8, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT]] +// CHECK-NEXT: ret i32 0 +// +int main(int argc, char **argv) { + union Foo1 f1; + union Foo2 f2; + union Foo3 f3; + +// ... +// CHECK: ![[ANNOT_BSS_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c b/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c new file mode 100644 index 0000000000000..e9a8dd92a458a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c @@ -0,0 +1,39 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 + +#include + +// X86_64-LABEL: @foo( +// X86_64-NEXT: entry: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTRARG:%.*]], i64 24, i1 false) +// X86_64-NEXT: ret void +// +// AARCH64-LABEL: @foo( +// AARCH64-NEXT: entry: +// AARCH64-NEXT: [[PTRARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// AARCH64-NEXT: store ptr [[PTRARG:%.*]], ptr [[PTRARG_INDIRECT_ADDR]], align 8 +// AARCH64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTRARG]], i64 24, i1 false) +// AARCH64-NEXT: ret void +// +// I686-LABEL: @foo( +// I686-NEXT: entry: +// I686-NEXT: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// I686-NEXT: store ptr [[AGG_RESULT:%.*]], ptr [[RESULT_PTR]], align 4 +// I686-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[PTRARG:%.*]], i32 12, i1 false) +// I686-NEXT: ret void +// +int*__bidi_indexable foo(int *__bidi_indexable ptrArg) { + return ptrArg; +} + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c b/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c new file mode 100644 index 0000000000000..c2901640fc1c6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c @@ -0,0 +1,20 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +// + +void *bar(void); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[A]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call ptr @bar() +// CHECK-NEXT: ret void +// +void foo(void) { + int * a = 0; + (unsigned*) bar(); +} diff --git a/clang/test/BoundsSafety/CodeGen/bound-compare.c b/clang/test/BoundsSafety/CodeGen/bound-compare.c new file mode 100644 index 0000000000000..61ca22da1b365 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bound-compare.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT + + +int main() { + int arr[10]; + int *a = arr; + if (a == 0) + return 0; + return 1; +} + +// NOPT-LABEL: @main( +// NOPT-NEXT: entry: +// NOPT-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// NOPT-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// NOPT-NEXT: [[A:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPT-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// NOPT-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// NOPT-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// NOPT-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 0 +// NOPT-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// NOPT-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 1 +// NOPT-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// NOPT-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 2 +// NOPT-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// NOPT-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[A]], i64 24, i1 false) +// NOPT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPT-NEXT: [[CMP:%.*]] = icmp eq ptr [[WIDE_PTR_PTR]], null +// NOPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// NOPT: if.then: +// NOPT-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// NOPT-NEXT: br label [[RETURN:%.*]] +// NOPT: if.end: +// NOPT-NEXT: store i32 1, ptr [[RETVAL]], align 4 +// NOPT-NEXT: br label [[RETURN]] +// NOPT: return: +// NOPT-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL]], align 4 +// NOPT-NEXT: ret i32 [[TMP3]] + +// OPT-LABEL: @main( +// OPT-NEXT: entry: +// OPT-NEXT: ret i32 1 +// diff --git a/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c b/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c new file mode 100644 index 0000000000000..24f61d41e3d33 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c @@ -0,0 +1,29 @@ + +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64-linux-gnu %s -o /dev/null + +#include + +int foo_vaarg(int num, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, num); + + int *__unsafe_indexable ptr = __builtin_va_arg(ap, int*); + return *ptr; +} + +int main() { + int a = 0; + int *ptr = &a; + foo_vaarg(1, ptr); + + int len; + int *__counted_by(len) countPtr; + foo_vaarg(1, countPtr); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c new file mode 100644 index 0000000000000..780da216f0dbb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s +#include "bounds-attributed-in-return-null-system-header.h" + +// FIXME: The IR for `inline_header_func_unspecified_ptr` and +// `inline_header_func_unsafe_indexable_ptr` are not included by +// `utils/update_cc_test_checks.py` but that's the IR we **actually** want to +// match. There doesn't seem to be a way to persuade the script to generate +// CHECK lines for it. + +void consume(int* __bidi_indexable); + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unspecified_ptr(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unspecified_ptr(i32 noundef [[TMP0]]) +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unspecified_ptr(int count) { + int* ptr = inline_header_func_unspecified_ptr(count); + consume(ptr); +} + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unsafe_indexable_ptr(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unsafe_indexable_ptr(i32 noundef [[TMP0]]) +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unsafe_indexable_ptr(int count) { + int* ptr = inline_header_func_unsafe_indexable_ptr(count); + consume(ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c new file mode 100644 index 0000000000000..45a4832cfd42e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s +#include "bounds-attributed-in-return-null-system-header.h" + +void consume(int* __bidi_indexable); + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[COUNT]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_I]], label %[[INLINE_HEADER_FUNC_UNSPECIFIED_PTR_EXIT:.*]], label %[[TRAP_I:.*]], !annotation [[META2]] +// CHECK: [[TRAP_I]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[INLINE_HEADER_FUNC_UNSPECIFIED_PTR_EXIT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr null, i64 [[IDX_EXT]] +// LEGACY-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR3:[0-9]+]] +// LEGACY-NEXT: store ptr null, ptr [[BYVAL_TEMP]], align 8 +// LEGACY-NEXT: [[PTR_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[PTR_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// LEGACY-NEXT: [[PTR_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// LEGACY-NEXT: store ptr null, ptr [[PTR_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 8, !tbaa [[TBAA2:![0-9]+]] +// LEGACY-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR3]] +// LEGACY-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR3]] +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unspecified_ptr(int count) { + int* ptr = inline_header_func_unspecified_ptr(count); + consume(ptr); +} + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_I]], label %[[INLINE_HEADER_FUNC_UNSAFE_INDEXABLE_PTR_EXIT:.*]], label %[[TRAP_I:.*]], !annotation [[META2]] +// CHECK: [[TRAP_I]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[INLINE_HEADER_FUNC_UNSAFE_INDEXABLE_PTR_EXIT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr null, i64 [[IDX_EXT]] +// LEGACY-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR3]] +// LEGACY-NEXT: store ptr null, ptr [[BYVAL_TEMP]], align 8 +// LEGACY-NEXT: [[PTR_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[PTR_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// LEGACY-NEXT: [[PTR_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// LEGACY-NEXT: store ptr null, ptr [[PTR_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// LEGACY-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR3]] +// LEGACY-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR3]] +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unsafe_indexable_ptr(int count) { + int* ptr = inline_header_func_unsafe_indexable_ptr(count); + consume(ptr); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. +// LEGACY: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// LEGACY: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0} +// LEGACY: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// LEGACY: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h new file mode 100644 index 0000000000000..de7a0ebee5da9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h @@ -0,0 +1,19 @@ +// Pretend this is a system header +#pragma clang system_header +#include + + +inline int* __counted_by(count) inline_header_func_unspecified_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. This is a case + // where an implicit cast to __bidi_indexable is needed. + return (int*)0; +} + +inline int* __counted_by(count) inline_header_func_unsafe_indexable_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. This is a case + // where an implicit cast to __bidi_indexable is needed. + return (int* __unsafe_indexable)0; +} + diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c new file mode 100644 index 0000000000000..069ce4daff018 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c @@ -0,0 +1,648 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi_redecl( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(*count) cb_in_from_bidi_redecl(int *count, int *__bidi_indexable p); +int *__counted_by(*count) cb_in_from_bidi_redecl(int *count, int *__bidi_indexable p) { + return p; +} + +int *__counted_by(*count) cb_in_from_bidi_typeof(int *count, int *__bidi_indexable p); +__typeof__(cb_in_from_bidi_typeof) cb_in_from_bidi_typeof; +extern __attribute__((weak_import)) __typeof__(cb_in_from_bidi_typeof) cb_in_from_bidi_typeof; + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi_typeof( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(*count) cb_in_from_bidi_typeof(int *count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP1]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP12]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP13]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB23]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB25]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP4]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR40]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr noundef [[END:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR18]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c new file mode 100644 index 0000000000000..d79ce79a54384 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c @@ -0,0 +1,308 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_indexable( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], [2 x i64] noundef [[P_COERCE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_COERCE_FCA_0_EXTRACT:%.*]] = extractvalue [2 x i64] [[P_COERCE]], 0 +// CHECK-NEXT: [[P_COERCE_FCA_1_EXTRACT:%.*]] = extractvalue [2 x i64] [[P_COERCE]], 1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt i64 [[P_COERCE_FCA_0_EXTRACT]], [[P_COERCE_FCA_1_EXTRACT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[P_COERCE_FCA_1_EXTRACT]], [[P_COERCE_FCA_0_EXTRACT]], !annotation [[META7]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP37:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP37]], [[CMP34]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[P_COERCE_FCA_0_EXTRACT]] to ptr +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[COUNT]], 2 +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cb( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cb(int count, int *__counted_by(count) p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cb2( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT:%.*]] = icmp ugt i32 [[COUNT]], [[LEN]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[SPEC_SELECT_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cb2(int count, int *__counted_by(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cbn( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP_SROA_9_0:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[TMP_SROA_9_0]], [[P]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_9_0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[P]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[IDX_EXT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cbn(int count, int *__counted_by_or_null(count) p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cbn2( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef returned [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META9]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP_SROA_9_0:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[TMP_SROA_9_0]], [[P]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_9_0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[P]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cbn2(int count, int *__counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_out_from_single( +// CHECK-SAME: ptr nocapture noundef readonly [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 4, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = icmp ult i32 [[TMP0]], 2, !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[COUNT]], 2 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[SPEC_SELECT]], [[TOBOOL_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[SIZE]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @sb_in_from_single( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[SIZE]], 5 +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +void *__sized_by(size) sb_in_from_single(int size, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_in_from_bidi( +// CHECK-SAME: ptr noundef readnone [[END:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +void *__ended_by(end) eb_in_from_bidi(void *__single end, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @eb_in_from_single( +// CHECK-SAME: ptr noundef readnone [[END:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp uge ptr [[UPPER]], [[END]], !annotation [[META2]] +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr [[P]], [[END]], !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP1]], [[CMP]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META9]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[TBAA10]] = !{[[META11:![0-9]+]], [[META11]], i64 0} +// CHECK: [[META11]] = !{!"int", [[META5]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c new file mode 100644 index 0000000000000..5eca9b3d54bd7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c @@ -0,0 +1,113 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr noundef [[END:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c new file mode 100644 index 0000000000000..05153eb4ff40f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c @@ -0,0 +1,65 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_out_from_single( +// CHECK-SAME: ptr nocapture noundef readnone [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr nocapture noundef readnone [[END:%.*]], ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c new file mode 100644 index 0000000000000..1b527a9686012 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c @@ -0,0 +1,126 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +// XFAIL: !rdar137311963 + +#include + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[BUF:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[BUF]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP14]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP14]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP15]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR34]] +// +int *__counted_by(count) foo(int count, int *__counted_by(count) buf) { + int *local = buf; + return ++local; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c new file mode 100644 index 0000000000000..eab84e53f9f39 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c @@ -0,0 +1,297 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @cb_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK-LABEL: define dso_local ptr @cb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local ptr @cb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @sb_0( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_0( +// LEGACY-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @sb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local ptr @sb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c new file mode 100644 index 0000000000000..6f5fbdf825423 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c @@ -0,0 +1,232 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s + +#include + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_0( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SIZE]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_0( +// LEGACY-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c b/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c new file mode 100644 index 0000000000000..08bd819148417 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E | FileCheck %s --check-prefix=DISABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -x objective-c | FileCheck %s --check-prefix=DISABLED + +#if __has_feature(bounds_attributes) +// ENABLED: has_bounds_attributes +void has_bounds_attributes() {} +#else +// DISABLED: no_bounds_attributes +void no_bounds_attributes() {} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/bounds-expr.c b/clang/test/BoundsSafety/CodeGen/bounds-expr.c new file mode 100644 index 0000000000000..dd9a8589b3150 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-expr.c @@ -0,0 +1,84 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +int top_level_array[10]; + +int *__unsafe_indexable top_level_lower = __builtin_get_pointer_lower_bound(top_level_array); +int *__unsafe_indexable top_level_upper = __builtin_get_pointer_upper_bound(top_level_array); +// CHECK: @top_level_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + +int *__unsafe_indexable top_level_added_lower = __builtin_get_pointer_lower_bound(top_level_array + 5); +int *__unsafe_indexable top_level_added_upper = __builtin_get_pointer_upper_bound(top_level_array + 5); +// CHECK: @top_level_added_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_added_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + +int *__unsafe_indexable top_level_indexed_lower = __builtin_get_pointer_lower_bound(&top_level_array[5]); +int *__unsafe_indexable top_level_indexed_upper = __builtin_get_pointer_upper_bound(&top_level_array[5]); +// CHECK: @top_level_indexed_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_indexed_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + + +struct subobject_array { + int foo; + int array[10]; + int bar; +}; + +struct subobject_array subobj; + +int *__unsafe_indexable subobject_lower = __builtin_get_pointer_lower_bound(subobj.array); +int *__unsafe_indexable subobject_upper = __builtin_get_pointer_upper_bound(subobj.array); +// CHECK: @subobject_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +int *__unsafe_indexable subobject_added_lower = __builtin_get_pointer_lower_bound(subobj.array + 5); +int *__unsafe_indexable subobject_added_upper = __builtin_get_pointer_upper_bound(subobj.array + 5); +// CHECK: @subobject_added_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_added_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +int *__unsafe_indexable subobject_indexed_lower = __builtin_get_pointer_lower_bound(&subobj.array[5]); +int *__unsafe_indexable subobject_indexed_upper = __builtin_get_pointer_upper_bound(&subobj.array[5]); +// CHECK: @subobject_indexed_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_indexed_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +struct subobject_array *__unsafe_indexable lower = __builtin_get_pointer_lower_bound(&subobj); +struct subobject_array *__unsafe_indexable upper = __builtin_get_pointer_upper_bound(&subobj); +// CHECK: @lower = global ptr @subobj, align 8 +// CHECK: @upper = global ptr getelementptr (i8, ptr @subobj, i64 48), align 8 + + +// CHECK-LABEL: @getLowerBound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable getLowerBound(int *__bidi_indexable ptr) { + return __builtin_get_pointer_lower_bound(ptr); +} + + +// CHECK-LABEL: @getUpperBound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable getUpperBound(int *__bidi_indexable ptr) { + return __builtin_get_pointer_upper_bound(ptr); +} + diff --git a/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c b/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c new file mode 100644 index 0000000000000..eb98912b2e623 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E | FileCheck %s --check-prefix=DISABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -x objective-c | FileCheck %s --check-prefix=DISABLED + +#if __has_feature(bounds_safety) +// ENABLED: has_bounds_safety +void has_bounds_safety() {} +#else +// DISABLED: no_bounds_safety +void no_bounds_safety() {} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c new file mode 100644 index 0000000000000..68cb5959d7778 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c @@ -0,0 +1,124 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 -fsanitize=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow -fsanitize-trap=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow %s -o - | FileCheck %s --check-prefix=UBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=NOUBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 -fsanitize=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow -fsanitize-trap=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow %s -o - | FileCheck %s --check-prefix=UBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=NOUBSAN + +#include + +void f_callee(char *__counted_by(inLen), unsigned long long inLen); +// UBSAN-LABEL: @f_unsafe_outlen( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DEROUT:%.*]], i64 [[TMP0]] +// UBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[TMP1]], [[DEROUT]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// UBSAN: trap: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// UBSAN: cont: +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT]], i64 noundef [[TMP0]]) #[[ATTR5:[0-9]+]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_unsafe_outlen( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// NOUBSAN-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DEROUT:%.*]], i64 [[TMP0]] +// NOUBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[TMP1]], [[DEROUT]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// NOUBSAN: trap: +// NOUBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// NOUBSAN: cont: +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT]], i64 noundef [[TMP0]]) #[[ATTR5:[0-9]+]] +// NOUBSAN-NEXT: ret void +// +void f_unsafe_outlen(char *__unsafe_indexable derOut, unsigned long long *outLen) { + f_callee(__unsafe_forge_bidi_indexable(char *, derOut, *outLen), *outLen); +} + +// UBSAN-LABEL: @f_outlen( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP0]], -1 +// UBSAN-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT:%.*]], i64 noundef [[TMP0]]) #[[ATTR5]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_outlen( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// NOUBSAN-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP0]], -1 +// NOUBSAN-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT:%.*]], i64 noundef [[TMP0]]) #[[ATTR5]] +// NOUBSAN-NEXT: ret void +// +void f_outlen(char *__counted_by(*outLen) derOut, unsigned long long *outLen) { + f_callee(derOut, *outLen); +} + +// UBSAN-LABEL: @f_call_bound_checks( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BUF:%.*]], align 8 +// UBSAN-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP_SROA_0_0_COPYLOAD]], i64 [[OFFSET:%.*]] +// UBSAN-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[TMP_SROA_0_0_COPYLOAD]] to i64, {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP1:%.*]] = add i64 [[TMP0]], [[OFFSET]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP_SROA_0_0_COPYLOAD]], null, {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP3:%.*]] = icmp ne i64 [[TMP1]], 0, {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP4:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP5:%.*]] = icmp uge i64 [[TMP1]], [[TMP0]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP6:%.*]] = and i1 [[TMP5]], [[TMP4]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!nosanitize ![0-9]+}} +// UBSAN: trap: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 19) #[[ATTR4]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// UBSAN: cont: +// UBSAN-NEXT: [[TMP_SROA_4_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BUF]], i64 16 +// UBSAN-NEXT: [[TMP_SROA_4_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_4_0_BUF_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[TMP_SROA_3_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BUF]], i64 8 +// UBSAN-NEXT: [[TMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_3_0_BUF_SROA_IDX]], align 8 +// UBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[TMP_SROA_4_0_COPYLOAD]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[OR_COND:%.*]] = select i1 [[CMP_NOT]], i1 true, i1 [[CMP27_NOT]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_3_0_COPYLOAD]] to i64, {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64, {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[CMP44_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[LEN:%.*]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[OR_COND55:%.*]] = or i1 [[OR_COND]], [[CMP44_NOT]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: br i1 [[OR_COND55]], label [[TRAP45:%.*]], label [[CONT46:%.*]], {{!annotation ![0-9]+}} +// UBSAN: trap45: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// UBSAN: cont46: +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[BOUND_PTR_ARITH]], i64 noundef [[LEN]]) #[[ATTR5]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_call_bound_checks( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BUF:%.*]], align 8 +// NOUBSAN-NEXT: [[TMP_SROA_2_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BUF]], i64 8 +// NOUBSAN-NEXT: [[TMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_2_0_BUF_SROA_IDX]], align 8 +// NOUBSAN-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP_SROA_0_0_COPYLOAD]], i64 [[OFFSET:%.*]] +// NOUBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[TMP_SROA_3_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BUF]], i64 16 +// NOUBSAN-NEXT: [[TMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_3_0_BUF_SROA_IDX]], align 8 +// NOUBSAN-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[TMP_SROA_3_0_COPYLOAD]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[OR_COND:%.*]] = select i1 [[CMP_NOT]], i1 true, i1 [[CMP27_NOT]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_2_0_COPYLOAD]] to i64, {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64, {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[CMP44_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[LEN:%.*]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[OR_COND52:%.*]] = or i1 [[OR_COND]], [[CMP44_NOT]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: br i1 [[OR_COND52]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// NOUBSAN: trap: +// NOUBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// NOUBSAN: cont: +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[BOUND_PTR_ARITH]], i64 noundef [[LEN]]) #[[ATTR5]] +// NOUBSAN-NEXT: ret void +// +void f_call_bound_checks(char *__bidi_indexable buf, unsigned long long offset, + unsigned long long len) { + f_callee(buf + offset, len); +} diff --git a/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c new file mode 100644 index 0000000000000..b703990f87343 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + char dst[10]; + char src[10]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestDstFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestDstFail(void) { + char dst[9]; + char src[10]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestSrcFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestSrcFail(void) { + char dst[10]; + char src[9]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail(void) { + char dst[10]; + char src[10]; + int len = 11; + __builtin_memcpy(dst, src, len); +} diff --git a/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..182e6bca8715b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c @@ -0,0 +1,279 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + + +// + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DST:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[SRC:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP81:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP92:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP101:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP110:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP111:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP127:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP147:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP154:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[DST]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[SRC]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[DST]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[SRC]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB19]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR21]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[CMP42:%.*]] = icmp ule ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_PTR37]] +// CHECK-NEXT: br i1 [[CMP42]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB46]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR48]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB50]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB52]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR68:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB70:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB72:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR71]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR54]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR68]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP73:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP73]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP81]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB83]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR84:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR85:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR84]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR86:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB87:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR86]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR88:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB89:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR88]], align 8 +// CHECK-NEXT: [[CMP90:%.*]] = icmp ule ptr [[WIDE_PTR_PTR76]], [[WIDE_PTR_PTR85]] +// CHECK-NEXT: br i1 [[CMP90]], label [[LAND_LHS_TRUE91:%.*]], label [[LAND_END144:%.*]] +// CHECK: land.lhs.true91: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP92]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR93:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB94:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR93]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB94]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR96:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR97:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB98:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR97]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR99:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB100:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR99]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP101]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR103:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR104:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB105:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR104]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR106:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB107:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR106]], align 8 +// CHECK-NEXT: [[CMP108:%.*]] = icmp ule ptr [[WIDE_PTR_PTR96]], [[WIDE_PTR_PTR103]] +// CHECK-NEXT: br i1 [[CMP108]], label [[LAND_RHS109:%.*]], label [[LAND_END144]] +// CHECK: land.rhs109: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP111]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR112:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB113:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR112]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB113]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR114:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR115:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR114]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR116:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB117:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR116]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR118:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB119:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR118]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR115]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB117]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB119]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP127]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR128:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR129:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR128]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR130:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB131:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR130]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR132:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB133:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR132]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR129]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB131]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB133]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR134:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR135:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR134]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR136:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB137:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR136]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR138:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB139:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR138]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST140:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR121]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST141:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR135]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB142:%.*]] = sub i64 [[SUB_PTR_LHS_CAST140]], [[SUB_PTR_RHS_CAST141]] +// CHECK-NEXT: [[CMP143:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB142]] +// CHECK-NEXT: br label [[LAND_END144]] +// CHECK: land.end144: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE91]] ], [ false, [[CONT]] ], [ [[CMP143]], [[LAND_RHS109]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT146:%.*]], label [[TRAP145:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap145: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont146: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP147]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR148:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR149:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR148]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR150:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB151:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR150]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR152:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB153:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR152]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP154]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR155:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR156:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR155]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR157:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB158:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR157]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR159:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB160:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR159]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[WIDE_PTR_PTR149]], ptr align 1 [[WIDE_PTR_PTR156]], i64 10, i1 false) +// CHECK-NEXT: call void @llvm.assume(i1 true) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[WIDE_PTR_PTR149]], i64 10 +// CHECK-NEXT: ret void +// +void foo(void) { + char *dst, *src; + __builtin_memcpy(dst, src, 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c b/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c new file mode 100644 index 0000000000000..10b597ce49528 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c @@ -0,0 +1,521 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_X64_O0 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_ARM64_O0 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_X64_O0 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_ARM64_O0 +#include + +#define memmove_(b, ...) __builtin___memmove_chk(b, __VA_ARGS__, b) +#define memmove(d, h, e) memmove_(d, h, e) + +int arr[] = {0, 1, 2, 3, 4, 5}; + + + +// CHECK_X64_O0-LABEL: @foo( +// CHECK_X64_O0-NEXT: entry: +// CHECK_X64_O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_X64_O0-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP83:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP84:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP100:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP119:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK_X64_O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK_X64_O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK_X64_O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK_X64_O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK_X64_O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK_X64_O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK_X64_O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK_X64_O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr @arr, ptr [[TMP9]], align 8 +// CHECK_X64_O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr getelementptr inbounds (i32, ptr @arr, i64 6), ptr [[TMP10]], align 8 +// CHECK_X64_O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr @arr, ptr [[TMP11]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK_X64_O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP12]], align 8 +// CHECK_X64_O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP13]], align 8 +// CHECK_X64_O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP14]], align 8 +// CHECK_X64_O0-NEXT: [[TMP15:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 4 +// CHECK_X64_O0-NEXT: [[IDX_EXT11:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK_X64_O0-NEXT: [[ADD_PTR12:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT11]] +// CHECK_X64_O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK_X64_O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[ADD_PTR12]], ptr [[TMP19]], align 8 +// CHECK_X64_O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK_X64_O0-NEXT: [[TMP21:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR14]] to i64 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK_X64_O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP22]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK_X64_O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR24]], ptr [[TMP23]], align 8 +// CHECK_X64_O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB26]], ptr [[TMP24]], align 8 +// CHECK_X64_O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP25]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK_X64_O0-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP26]], align 8 +// CHECK_X64_O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP27]], align 8 +// CHECK_X64_O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB42]], ptr [[TMP28]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK_X64_O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR30]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK_X64_O0-NEXT: [[CMP:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_X64_O0: trap: +// CHECK_X64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: unreachable +// CHECK_X64_O0: cont: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK_X64_O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP29]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK_X64_O0-NEXT: [[CMP65:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR60]] +// CHECK_X64_O0-NEXT: br i1 [[CMP65]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK_X64_O0: land.lhs.true: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK_X64_O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB68]], ptr [[TMP30]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8 +// CHECK_X64_O0-NEXT: [[CMP82:%.*]] = icmp ule ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_PTR77]] +// CHECK_X64_O0-NEXT: br i1 [[CMP82]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK_X64_O0: land.rhs: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP84]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8 +// CHECK_X64_O0-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB86]], ptr [[TMP31]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR88:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR87]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR89:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB90:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR89]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR91:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB92:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR91]], align 8 +// CHECK_X64_O0-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR88]], ptr [[TMP32]], align 8 +// CHECK_X64_O0-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB90]], ptr [[TMP33]], align 8 +// CHECK_X64_O0-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB92]], ptr [[TMP34]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP100]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR101:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR102:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR101]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR103:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB104:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR103]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR105:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB106:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR105]], align 8 +// CHECK_X64_O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR102]], ptr [[TMP35]], align 8 +// CHECK_X64_O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB104]], ptr [[TMP36]], align 8 +// CHECK_X64_O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB106]], ptr [[TMP37]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK_X64_O0-NEXT: [[SUB_PTR_LHS_CAST113:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR94]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_RHS_CAST114:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR108]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_SUB115:%.*]] = sub i64 [[SUB_PTR_LHS_CAST113]], [[SUB_PTR_RHS_CAST114]] +// CHECK_X64_O0-NEXT: [[CMP116:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB115]] +// CHECK_X64_O0-NEXT: br label [[LAND_END]] +// CHECK_X64_O0: land.end: +// CHECK_X64_O0-NEXT: [[TMP38:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP116]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: br i1 [[TMP38]], label [[CONT118:%.*]], label [[TRAP117:%.*]], {{!annotation ![0-9]+}} +// CHECK_X64_O0: trap117: +// CHECK_X64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: unreachable +// CHECK_X64_O0: cont118: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP119]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP126]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR127:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR128:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR127]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR129:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB130:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR129]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR131:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB132:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR131]], align 8 +// CHECK_X64_O0-NEXT: [[CALL:%.*]] = call ptr @__memmove_chk(ptr noundef [[WIDE_PTR_PTR121]], ptr noundef [[WIDE_PTR_PTR128]], i64 noundef 24, i64 noundef [[TMP21]]) #[[ATTR6:[0-9]+]] +// CHECK_X64_O0-NEXT: call void @llvm.assume(i1 true) +// CHECK_X64_O0-NEXT: [[ADD_PTR133:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 24 +// CHECK_X64_O0-NEXT: ret i32 0 +// +// CHECK_ARM64_O0-LABEL: @foo( +// CHECK_ARM64_O0-NEXT: entry: +// CHECK_ARM64_O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_ARM64_O0-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP83:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP84:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP100:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP119:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK_ARM64_O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK_ARM64_O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK_ARM64_O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr @arr, ptr [[TMP9]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr getelementptr inbounds (i32, ptr @arr, i64 6), ptr [[TMP10]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr @arr, ptr [[TMP11]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP12]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP13]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP14]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP15:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 4 +// CHECK_ARM64_O0-NEXT: [[IDX_EXT11:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK_ARM64_O0-NEXT: [[ADD_PTR12:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT11]] +// CHECK_ARM64_O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[ADD_PTR12]], ptr [[TMP19]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP21:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR14]] to i64 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP22]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR24]], ptr [[TMP23]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB26]], ptr [[TMP24]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP25]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP26]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP27]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB42]], ptr [[TMP28]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR30]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK_ARM64_O0-NEXT: [[CMP:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0: trap: +// CHECK_ARM64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: unreachable +// CHECK_ARM64_O0: cont: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP29]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK_ARM64_O0-NEXT: [[CMP65:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR60]] +// CHECK_ARM64_O0-NEXT: br i1 [[CMP65]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK_ARM64_O0: land.lhs.true: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB68]], ptr [[TMP30]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8 +// CHECK_ARM64_O0-NEXT: [[CMP82:%.*]] = icmp ule ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_PTR77]] +// CHECK_ARM64_O0-NEXT: br i1 [[CMP82]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK_ARM64_O0: land.rhs: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP84]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB86]], ptr [[TMP31]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR88:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR87]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR89:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB90:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR89]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR91:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB92:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR91]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR88]], ptr [[TMP32]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB90]], ptr [[TMP33]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB92]], ptr [[TMP34]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP100]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR101:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR102:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR101]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR103:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB104:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR103]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR105:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB106:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR105]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR102]], ptr [[TMP35]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB104]], ptr [[TMP36]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB106]], ptr [[TMP37]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_LHS_CAST113:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR94]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_RHS_CAST114:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR108]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_SUB115:%.*]] = sub i64 [[SUB_PTR_LHS_CAST113]], [[SUB_PTR_RHS_CAST114]] +// CHECK_ARM64_O0-NEXT: [[CMP116:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB115]] +// CHECK_ARM64_O0-NEXT: br label [[LAND_END]] +// CHECK_ARM64_O0: land.end: +// CHECK_ARM64_O0-NEXT: [[TMP38:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP116]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: br i1 [[TMP38]], label [[CONT118:%.*]], label [[TRAP117:%.*]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0: trap117: +// CHECK_ARM64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: unreachable +// CHECK_ARM64_O0: cont118: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP119]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP126]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR127:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR128:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR127]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR129:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB130:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR129]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR131:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB132:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR131]], align 8 +// CHECK_ARM64_O0-NEXT: [[CALL:%.*]] = call ptr @__memmove_chk(ptr noundef [[WIDE_PTR_PTR121]], ptr noundef [[WIDE_PTR_PTR128]], i64 noundef 24, i64 noundef [[TMP21]]) #[[ATTR6:[0-9]+]] +// CHECK_ARM64_O0-NEXT: call void @llvm.assume(i1 true) +// CHECK_ARM64_O0-NEXT: [[ADD_PTR133:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 24 +// CHECK_ARM64_O0-NEXT: ret i32 0 +// +int foo(int *__counted_by(*len) buf, int *len) { + memmove(buf, arr, sizeof(arr)); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c new file mode 100644 index 0000000000000..14c44749de6e0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c @@ -0,0 +1,33 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP3]] +// +int *__indexable test(int *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c new file mode 100644 index 0000000000000..a134c70b90c32 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c @@ -0,0 +1,26 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[AGG_TEMP_SROA_2_0_COPYLOAD]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test(int *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c new file mode 100644 index 0000000000000..4e957ecded331 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c @@ -0,0 +1,74 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[42]; + int array[3]; + int _b[42]; +} foo; + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good(void) { + int *__bidi_indexable p = foo.array; + return p; +} + +// CHECK-LABEL: @good_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @foo, i64 176), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_upper(void) { + int *__bidi_indexable p = foo.array; + p += 2; + return p; +} + +// CHECK-LABEL: @good_upper2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @foo, i64 208), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_upper2(void) { + int *__bidi_indexable p = foo.array; + p += 10; + return p; +} + +// CHECK-LABEL: @bad_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_lower(void) { + int *__bidi_indexable p = foo.array; + p -= 1; + return p; +} + +// CHECK-LABEL: @good_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } zeroinitializer +// +int *__indexable good_null(void) { + int *__bidi_indexable p = 0; + return p; +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_null(void) { + // Try to cast { null, non-null, non-null } and make sure that we don't + // return { null, non-null }. + int *__bidi_indexable p = foo.array; + while (--p) + ; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c new file mode 100644 index 0000000000000..d7e647eb040bf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c @@ -0,0 +1,30 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable test(int *__indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c new file mode 100644 index 0000000000000..5531fa3ce6546 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c @@ -0,0 +1,19 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable test(int *__indexable p) { + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/cast-member-expr.c b/clang/test/BoundsSafety/CodeGen/cast-member-expr.c new file mode 100644 index 0000000000000..1fe2ff1683a93 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-member-expr.c @@ -0,0 +1,10 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +struct ctx { int x; }; + +void foo(void) { + int *p = 0; + ((struct ctx *)p)->x = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c new file mode 100644 index 0000000000000..8c411431aa81e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c @@ -0,0 +1,273 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +void Foo(int *__counted_by(len) buf, int len); +void Bar(int *__counted_by(10) ptr); + +// CHECK-LABEL: @TestFoo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 11 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 1, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @Foo(ptr noundef [[WIDE_PTR_PTR49]], i32 noundef 1) +// CHECK-NEXT: ret void +// +void TestFoo(void) { + int arr[10]; + int *end_ptr = arr + 11; +here: + Foo(end_ptr, 1); // trap +} + +// CHECK-LABEL: @TestBar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @Bar(ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +void TestBar(void) { + int arr[10]; + int *end_ptr = arr + 1; +here: + Bar(({end_ptr;})); // trap (needs 10 elements) +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c new file mode 100644 index 0000000000000..85d6f8a918c44 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c @@ -0,0 +1,181 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__counted_by(len) buf, int len); +void Bar(int *__counted_by(10) ptr); + +// CHECK-LABEL: @TestZeroCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestZeroCount(void) { + int arr[10]; + int *end_ptr = arr + 10; + + S s; + s.len = 0; + s.buf = end_ptr; // okay +} + +// CHECK-LABEL: @TestCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 10); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail2(void) { + int arr[10]; + int *end_ptr = arr + 11; + + Foo(end_ptr, 10); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail3(void) { + int arr[10]; + int n = 12; + Foo(arr, n); // trap +} + +// CHECK-LABEL: @TestCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], i32 noundef 0) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(end_ptr, 0); // ok +} + +// CHECK-LABEL: @TestCountPtrArgOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[BOUND_PTR_ARITH]], i32 noundef 1) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK2(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 1); // ok +} + +// CHECK-LABEL: @TestConstCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Bar(({end_ptr;})); // trap +} + +// CHECK-LABEL: @TestConstCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @Bar(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestConstCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr; + + Bar(({end_ptr;})); // ok +} + +// CHECK-LABEL: @TestConstZeroCountOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestConstZeroCountOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + int *__counted_by(0) zero_buf; + zero_buf = end_ptr; // ok +} + +// CHECK-LABEL: @TestBidiOverBounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestBidiOverBounds() { + int arr[10]; + Foo(arr + 11, 10); +} + +// XXX: unexpectedly this still has a bounds check +// CHECK-LABEL: @TestBidiUnderBounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP27_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @Foo(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef 10) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestBidiUnderBounds() { + int arr[10]; + Foo(arr - 1, 10); +} + +// XXX: this might be just fine not to trap +// CHECK-LABEL: @TestConstZeroCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstZeroCountFail(void) { + int arr[10]; + int *end_ptr = arr + 11; + + int *__counted_by(0) zero_buf; + zero_buf = end_ptr; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c new file mode 100644 index 0000000000000..d45c4a87e2fae --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c @@ -0,0 +1,102 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__indexable ptr); + +// + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], ptr noundef nonnull [[UPPER]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void FooCount1(int *__counted_by(1) ptr); + +// CHECK-LABEL: @TestCountArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + FooCount1(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void FooSingle(int *ptr); + +// CHECK-LABEL: @TestSingleArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestSingleArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + FooSingle(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void *__sized_by(siz) my_alloc(unsigned long siz); + +// CHECK-LABEL: @TestCountArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i64 noundef 10) #[[ATTR5]] +// CHECK-NEXT: tail call void @FooCount1(ptr noundef [[CALL]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestCountArgOK(void) { + FooCount1(my_alloc(10)); +} + + + + + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c new file mode 100644 index 0000000000000..30b93a1583f96 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c @@ -0,0 +1,197 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__indexable ptr); + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_LB18]], [[WIDE_PTR_PTR21]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS:%.*]], label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB29]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP37]], label [[LAND_RHS38:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs38: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS38]] ] +// CHECK-NEXT: br label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.end39: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT41:%.*]], label [[TRAP40:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap40: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: [[BUF49:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[BUF49]], align 8 +// CHECK-NEXT: [[LEN50:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN50]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[S]], i64 16, i1 false) +// CHECK-NEXT: [[LEN52:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[LEN52]], align 8 +// CHECK-NEXT: [[BUF53:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[BUF53]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP18]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR55]], [[WIDE_PTR_LB59]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT61:%.*]], label [[TRAP60:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap60: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont61: +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR55]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB57]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP26:%.*]] = load ptr, ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: call void @Foo(ptr noundef [[TMP26]], ptr noundef [[TMP28]]) +// CHECK-NEXT: ret i32 0 +// +int Test(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c new file mode 100644 index 0000000000000..edf8ffa7b9a46 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c @@ -0,0 +1,147 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__sized_by(len) buf; +} S; + +void Foo(int *__sized_by(len) buf, int len); +void Bar(int *__sized_by(40) ptr); + +// CHECK-LABEL: @TestZeroCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestZeroCount(void) { + int arr[10]; + int *end_ptr = arr + 10; + + S s; + s.len = 0; + s.buf = end_ptr; // okay +} + +// CHECK-LABEL: @TestCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 40); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail2(void) { + int arr[10]; + int *end_ptr = arr + 11; + + Foo(end_ptr, 40); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail3(void) { + int arr[10]; + int n = 41; + Foo(arr, n); // trap +} + +// CHECK-LABEL: @TestCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], i32 noundef 0) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(end_ptr, 0); // ok +} + +// CHECK-LABEL: @TestCountPtrArgOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[BOUND_PTR_ARITH]], i32 noundef 4) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK2(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 4); // ok +} + +// CHECK-LABEL: @TestConstCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Bar(({end_ptr;})); // trap +} + +// CHECK-LABEL: @TestConstCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @Bar(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestConstCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr; + + Bar(({end_ptr;})); // ok +} + +// CHECK-LABEL: @TestConstZeroCountOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestConstZeroCountOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + int *__sized_by(0) zero_buf; + zero_buf = end_ptr; // ok +} + +// XXX: this might be just fine not to trap +// CHECK-LABEL: @TestConstZeroCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstZeroCountFail(void) { + int arr[10]; + int *end_ptr = arr + 11; + + int *__sized_by(0) zero_buf; + zero_buf = end_ptr; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c new file mode 100644 index 0000000000000..e55281e2430b8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c @@ -0,0 +1,70 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + unsigned count; + int *__counted_by(count) bytes; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + + f.bytes += amt; + f.count -= amt; +} + +// CHECK-LABEL: @TestOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK2() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + + f.count -= amt; + f.bytes += amt; +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + unsigned amt_1 = 9; + + f.bytes += amt; + f.count -= amt_1; +} + +// CHECK-LABEL: @TestFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail2() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + unsigned amt_1 = 9; + + f.count -= amt_1; + f.bytes += amt; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c new file mode 100644 index 0000000000000..7f7838deaff7a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c @@ -0,0 +1,33 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + + +#include + +struct foo { + unsigned count; + unsigned char *__counted_by(count) bytes; +}; +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK(void) { + unsigned char arr[10]; + struct foo f = {10, arr}; + f.bytes = f.bytes + 10; + f.count = f.count - 10; +} + +// CHECK-O2-LABEL: @TestFail( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail(void) { + unsigned char arr[10]; + struct foo f = {10, arr}; + f.bytes = f.bytes + 10; + f.count = f.count - 9; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c new file mode 100644 index 0000000000000..3145714e8ece5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c @@ -0,0 +1,122 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct foo { + unsigned count; + unsigned char *__counted_by(count) bytes; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BYTES:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BYTES]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP16]], 10 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP27]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[BYTES36:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP18]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[BYTES36]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT37:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP19]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT37]], align 8 +// CHECK-NEXT: ret void +// +void foo(struct foo *f) { + f->bytes = f->bytes + 10; + f->count = f->count - 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..9e258e501776c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c @@ -0,0 +1,1301 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP6]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP6]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c new file mode 100644 index 0000000000000..ea085f8e50320 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c @@ -0,0 +1,2999 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP3]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP3]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_LHS_TRUE43:.*]], label %[[LAND_END77:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp ule ptr [[WIDE_PTR_LB46]], [[WIDE_PTR_PTR49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP54]], label %[[LAND_RHS56:.*]], label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_RHS56]]: +// CHECK-NEXT: [[CONV57:%.*]] = sext i32 [[TMP4]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST68:%.*]] = ptrtoint ptr [[WIDE_PTR_UB60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR63]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB70:%.*]] = sub i64 [[SUB_PTR_LHS_CAST68]], [[SUB_PTR_RHS_CAST69]], !annotation [[META2]] +// CHECK-NEXT: [[CMP71:%.*]] = icmp sle i64 [[CONV57]], [[SUB_PTR_SUB70]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP71]], label %[[LAND_RHS73:.*]], label %[[LAND_END76:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS73]]: +// CHECK-NEXT: [[CMP74:%.*]] = icmp sle i32 0, [[TMP4]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END76]], !annotation [[META2]] +// CHECK: [[LAND_END76]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_RHS56]] ], [ [[CMP74]], %[[LAND_RHS73]] ] +// CHECK-NEXT: br label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_END77]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE43]] ], [ false, %[[CONT]] ], [ [[TMP5]], %[[LAND_END76]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT87:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT87]], align 4 +// CHECK-NEXT: [[BUF88:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR91]], ptr [[BUF88]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META5]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT59]], label %[[TRAP58:.*]], !annotation [[META6]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT32:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT32]], align 8 +// CHECK-NEXT: [[BUF33:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP8]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP8]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META5]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT59]], label %[[TRAP58:.*]], !annotation [[META6]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..2b09adb53c502 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c @@ -0,0 +1,636 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readnone [[PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META9]] = !{!"cb", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA10]] = !{[[META9]], [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META12]] = !{!"cb_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA13]] = !{[[META12]], [[META7]], i64 8} +// CHECK: [[TBAA14]] = !{[[META12]], [[META3]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c new file mode 100644 index 0000000000000..cf5ed59763b4e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c @@ -0,0 +1,1056 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp slt i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT99:%.*]] = or i1 [[CMP28]], [[CMP25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[SPEC_SELECT_NOT99]], label %[[TRAP]], label %[[CONT78:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_297_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_297_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF40]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF40]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META5]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META13]] = !{!"cb", [[META10]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA14]] = !{[[META13]], [[META4]], i64 8} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META16]] = !{!"cb_with_other_data", [[META10]], i64 0, [[META4]], i64 8, [[META10]], i64 16} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META16]], [[META10]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c new file mode 100644 index 0000000000000..8337d8d8049bb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c @@ -0,0 +1,1443 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP10]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP10]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[BOUNDSCHECK_NOTNULL17:.*]], label %[[CONT19:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL17]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT19]], label %[[TRAP18:.*]], !annotation [[META5]] +// CHECK: [[TRAP18]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT19]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c new file mode 100644 index 0000000000000..70a120b858991 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c @@ -0,0 +1,3491 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP4]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP4]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP106:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP48:%.*]] = icmp ule ptr [[WIDE_PTR_PTR40]], [[WIDE_PTR_UB47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP48]], label %[[LAND_LHS_TRUE50:.*]], label %[[LAND_END94:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE50]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP61]], label %[[LAND_RHS63:.*]], label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_RHS63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL71:%.*]] = icmp ne ptr [[WIDE_PTR_PTR66]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL71]], label %[[LOR_RHS72:.*]], label %[[LOR_END93:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS72]]: +// CHECK-NEXT: [[CONV73:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST84:%.*]] = ptrtoint ptr [[WIDE_PTR_UB76]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST85:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR79]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB86:%.*]] = sub i64 [[SUB_PTR_LHS_CAST84]], [[SUB_PTR_RHS_CAST85]], !annotation [[META2]] +// CHECK-NEXT: [[CMP87:%.*]] = icmp sle i64 [[CONV73]], [[SUB_PTR_SUB86]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP87]], label %[[LAND_RHS89:.*]], label %[[LAND_END92:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS89]]: +// CHECK-NEXT: [[CMP90:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END92]], !annotation [[META2]] +// CHECK: [[LAND_END92]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LOR_RHS72]] ], [ [[CMP90]], %[[LAND_RHS89]] ] +// CHECK-NEXT: br label %[[LOR_END93]], !annotation [[META2]] +// CHECK: [[LOR_END93]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ true, %[[LAND_RHS63]] ], [ [[TMP6]], %[[LAND_END92]] ] +// CHECK-NEXT: br label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_END94]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE50]] ], [ false, %[[CONT]] ], [ [[TMP7]], %[[LOR_END93]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT96:.*]], label %[[TRAP95:.*]], !annotation [[META2]] +// CHECK: [[TRAP95]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT96]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP97]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR98:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR99:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB101:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB103:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR102]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR99]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT104:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT104]], align 4 +// CHECK-NEXT: [[BUF105:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP106]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR108]], ptr [[BUF105]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT64:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !annotation [[META5]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT64]], label %[[TRAP63:.*]], !annotation [[META6]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END38:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label %[[LAND_RHS35:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS35]]: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP36]], %[[LAND_RHS35]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP13]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_END38]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP14]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT39:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT39]], align 8 +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[BUF40]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP13]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP13]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[BOUNDSCHECK_NOTNULL61:.*]], label %[[CONT65:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL61]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !annotation [[META5]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT65]], label %[[TRAP64:.*]], !annotation [[META6]] +// CHECK: [[TRAP64]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT65]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c new file mode 100644 index 0000000000000..6e819fbfd08c1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c @@ -0,0 +1,635 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META9]] = !{!"cbon", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA10]] = !{[[META9]], [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META12]] = !{!"cbon_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA13]] = !{[[META12]], [[META7]], i64 8} +// CHECK: [[TBAA14]] = !{[[META12]], [[META3]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c new file mode 100644 index 0000000000000..83ab30c4e5d38 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c @@ -0,0 +1,1118 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT95:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: [[CONV73:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[CMP87:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV73]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[SPEC_SELECT]], i1 [[CMP87]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT95]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT95]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2114_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_2114_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META19:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT51:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT51]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT49:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT49]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META5]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META13]] = !{!"cbon", [[META10]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA14]] = !{[[META13]], [[META4]], i64 8} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META16]] = !{!"cbon_with_other_data", [[META10]], i64 0, [[META4]], i64 8, [[META10]], i64 16} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META16]], [[META10]], i64 16} +// CHECK: [[META19]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..e05058e60c364 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c @@ -0,0 +1,1456 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[END9]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 40, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[END2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP12]]) +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP12]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[START8:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START8]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END9]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ne ptr [[WIDE_PTR_PTR19]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT25:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp ult ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_UB21]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp uge ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_LB23]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT25]], label %[[TRAP24:.*]], !annotation [[META5]] +// CHECK: [[TRAP24]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT25]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR19]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c new file mode 100644 index 0000000000000..2db6c869544dd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c @@ -0,0 +1,2823 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START16:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[START16]], align 8 +// CHECK-NEXT: [[END24:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END24]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP3]]) +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP3]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB18]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_LHS_TRUE20:.*]], label %[[LAND_END41:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE20]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS29:.*]], label %[[LAND_END41]], !annotation [[META2]] +// CHECK: [[LAND_RHS29]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP40:%.*]] = icmp ule ptr [[WIDE_PTR_LB32]], [[WIDE_PTR_PTR35]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END41]], !annotation [[META2]] +// CHECK: [[LAND_END41]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE20]] ], [ false, %[[CONT]] ], [ [[CMP40]], %[[LAND_RHS29]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT43:.*]], label %[[TRAP42:.*]], !annotation [[META2]] +// CHECK: [[TRAP42]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT43]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[START51:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[START51]], align 8 +// CHECK-NEXT: [[END59:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END59]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 40, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT24:.*]], label %[[TRAP23:.*]], !annotation [[META2]] +// CHECK: [[TRAP23]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT24]]: +// CHECK-NEXT: [[START25:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START25]], align 8 +// CHECK-NEXT: [[END26:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END26]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB34]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR36]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT44:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR36]], [[WIDE_PTR_UB38]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT42:.*]], label %[[TRAP41:.*]], !annotation [[META5]] +// CHECK: [[TRAP41]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT42]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR36]], [[WIDE_PTR_LB40]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT44]], label %[[TRAP43:.*]], !annotation [[META6]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START31:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[START31]], align 8 +// CHECK-NEXT: [[END39:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[END39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP13]]) +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP13]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT46:.*]], label %[[TRAP45:.*]], !annotation [[META2]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[START47:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START47]], align 8 +// CHECK-NEXT: [[END48:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END48]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR52]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB54]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB56]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR58:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB62:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR61]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR58]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT66:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR58]], [[WIDE_PTR_UB60]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !annotation [[META5]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR58]], [[WIDE_PTR_LB62]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT66]], label %[[TRAP65:.*]], !annotation [[META6]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR58]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[_NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[_NAMED_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..f86ddc9126200 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c @@ -0,0 +1,627 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_410_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_410_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 32 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[ARR]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr nocapture noundef readnone [[NEW_START:%.*]], ptr nocapture noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[ARR]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"int", [[META4]], i64 0} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META9]] = !{!"eb", [[META3]], i64 0, [[META3]], i64 8} +// CHECK: [[TBAA10]] = !{[[META9]], [[META3]], i64 8} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META12]] = !{!"eb_with_other_data", [[META3]], i64 0, [[META3]], i64 8, [[META7]], i64 16} +// CHECK: [[TBAA13]] = !{[[META12]], [[META3]], i64 8} +// CHECK: [[TBAA14]] = !{[[META12]], [[META7]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c new file mode 100644 index 0000000000000..275270a331114 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c @@ -0,0 +1,936 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT42:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT42]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_460_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_460_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 32 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[ARR]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_1_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef readnone [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef readnone [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef readnone [[NEW_START:%.*]], ptr noundef readnone [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[ARR]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META5]], i64 0} +// CHECK: [[META9]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA10]] = !{[[META11:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META11]] = !{!"eb", [[META4]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA12]] = !{[[META11]], [[META4]], i64 8} +// CHECK: [[TBAA13]] = !{[[META14:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META14]] = !{!"eb_with_other_data", [[META4]], i64 0, [[META4]], i64 8, [[META8]], i64 16} +// CHECK: [[TBAA15]] = !{[[META14]], [[META4]], i64 8} +// CHECK: [[TBAA16]] = !{[[META14]], [[META8]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..4f4210db01598 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c @@ -0,0 +1,1303 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP6]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP6]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c new file mode 100644 index 0000000000000..c124b8079e60f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c @@ -0,0 +1,3001 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP3]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP3]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_LHS_TRUE43:.*]], label %[[LAND_END77:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp ule ptr [[WIDE_PTR_LB46]], [[WIDE_PTR_PTR49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP54]], label %[[LAND_RHS56:.*]], label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_RHS56]]: +// CHECK-NEXT: [[CONV57:%.*]] = sext i32 [[TMP4]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST68:%.*]] = ptrtoint ptr [[WIDE_PTR_UB60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR63]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB70:%.*]] = sub i64 [[SUB_PTR_LHS_CAST68]], [[SUB_PTR_RHS_CAST69]], !annotation [[META2]] +// CHECK-NEXT: [[CMP71:%.*]] = icmp sle i64 [[CONV57]], [[SUB_PTR_SUB70]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP71]], label %[[LAND_RHS73:.*]], label %[[LAND_END76:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS73]]: +// CHECK-NEXT: [[CMP74:%.*]] = icmp sle i32 0, [[TMP4]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END76]], !annotation [[META2]] +// CHECK: [[LAND_END76]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_RHS56]] ], [ [[CMP74]], %[[LAND_RHS73]] ] +// CHECK-NEXT: br label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_END77]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE43]] ], [ false, %[[CONT]] ], [ [[TMP5]], %[[LAND_END76]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT87:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT87]], align 4 +// CHECK-NEXT: [[BUF88:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR91]], ptr [[BUF88]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META5]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT59]], label %[[TRAP58:.*]], !annotation [[META6]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT32:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT32]], align 8 +// CHECK-NEXT: [[BUF33:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP8]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP8]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META5]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT59]], label %[[TRAP58:.*]], !annotation [[META6]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..128f65caff417 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c @@ -0,0 +1,638 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readnone [[PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META9]] = !{!"sb", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA10]] = !{[[META9]], [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META12]] = !{!"sb_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA13]] = !{[[META12]], [[META7]], i64 8} +// CHECK: [[TBAA14]] = !{[[META12]], [[META3]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c new file mode 100644 index 0000000000000..8b16de1ce00b9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c @@ -0,0 +1,1058 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp slt i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT99:%.*]] = or i1 [[CMP28]], [[CMP25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[SPEC_SELECT_NOT99]], label %[[TRAP]], label %[[CONT78:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_297_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_297_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF40]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF40]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_238_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META5]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META13]] = !{!"sb", [[META10]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA14]] = !{[[META13]], [[META4]], i64 8} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META16]] = !{!"sb_with_other_data", [[META10]], i64 0, [[META4]], i64 8, [[META10]], i64 16} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META16]], [[META10]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c new file mode 100644 index 0000000000000..62fc32ec510f4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c @@ -0,0 +1,1443 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !annotation [[META5]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP10]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP10]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[BOUNDSCHECK_NOTNULL17:.*]], label %[[CONT19:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL17]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT19]], label %[[TRAP18:.*]], !annotation [[META5]] +// CHECK: [[TRAP18]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT19]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c new file mode 100644 index 0000000000000..cc7f06a55ea5c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c @@ -0,0 +1,3491 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP4]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP4]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP106:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP48:%.*]] = icmp ule ptr [[WIDE_PTR_PTR40]], [[WIDE_PTR_UB47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP48]], label %[[LAND_LHS_TRUE50:.*]], label %[[LAND_END94:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE50]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP61]], label %[[LAND_RHS63:.*]], label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_RHS63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL71:%.*]] = icmp ne ptr [[WIDE_PTR_PTR66]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL71]], label %[[LOR_RHS72:.*]], label %[[LOR_END93:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS72]]: +// CHECK-NEXT: [[CONV73:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST84:%.*]] = ptrtoint ptr [[WIDE_PTR_UB76]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST85:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR79]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB86:%.*]] = sub i64 [[SUB_PTR_LHS_CAST84]], [[SUB_PTR_RHS_CAST85]], !annotation [[META2]] +// CHECK-NEXT: [[CMP87:%.*]] = icmp sle i64 [[CONV73]], [[SUB_PTR_SUB86]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP87]], label %[[LAND_RHS89:.*]], label %[[LAND_END92:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS89]]: +// CHECK-NEXT: [[CMP90:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END92]], !annotation [[META2]] +// CHECK: [[LAND_END92]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LOR_RHS72]] ], [ [[CMP90]], %[[LAND_RHS89]] ] +// CHECK-NEXT: br label %[[LOR_END93]], !annotation [[META2]] +// CHECK: [[LOR_END93]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ true, %[[LAND_RHS63]] ], [ [[TMP6]], %[[LAND_END92]] ] +// CHECK-NEXT: br label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_END94]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE50]] ], [ false, %[[CONT]] ], [ [[TMP7]], %[[LOR_END93]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT96:.*]], label %[[TRAP95:.*]], !annotation [[META2]] +// CHECK: [[TRAP95]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT96]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP97]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR98:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR99:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB101:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB103:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR102]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR99]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT104:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT104]], align 4 +// CHECK-NEXT: [[BUF105:%.*]] = getelementptr inbounds [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP106]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR108]], ptr [[BUF105]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT64:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !annotation [[META5]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT64]], label %[[TRAP63:.*]], !annotation [[META6]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END38:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label %[[LAND_RHS35:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS35]]: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP36]], %[[LAND_RHS35]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP13]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_END38]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP14]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT39:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT39]], align 8 +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[BUF40]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP13]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP13]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[BOUNDSCHECK_NOTNULL61:.*]], label %[[CONT65:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL61]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !annotation [[META5]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT65]], label %[[TRAP64:.*]], !annotation [[META6]] +// CHECK: [[TRAP64]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT65]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c new file mode 100644 index 0000000000000..8c340f6012e50 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c @@ -0,0 +1,635 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_210_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA13]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_21_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META9]] = !{!"sbon", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA10]] = !{[[META9]], [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META12]] = !{!"sbon_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA13]] = !{[[META12]], [[META7]], i64 8} +// CHECK: [[TBAA14]] = !{[[META12]], [[META3]], i64 16} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c new file mode 100644 index 0000000000000..79b5564a2582e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c @@ -0,0 +1,1118 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT95:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: [[CONV73:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[CMP87:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV73]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[SPEC_SELECT]], i1 [[CMP87]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT95]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT95]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2114_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_2114_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr nocapture noundef readonly [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META19:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT51:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT51]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT49:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT49]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYINIT_ELEMENT]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META19]] +// CHECK-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr nocapture noundef writeonly [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readnone [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_21_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr nocapture noundef readonly [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_245_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META5]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META13]] = !{!"sbon", [[META10]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA14]] = !{[[META13]], [[META4]], i64 8} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META16]] = !{!"sbon_with_other_data", [[META10]], i64 0, [[META4]], i64 8, [[META10]], i64 16} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META16]], [[META10]], i64 16} +// CHECK: [[META19]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c b/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c new file mode 100644 index 0000000000000..de1d91b16c307 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c @@ -0,0 +1,222 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(); + +// CHECK-LABEL: @TestCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @foo() +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP14]], 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP26]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[IDX_EXT28:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: [[ADD_PTR29:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT28]] +// CHECK-NEXT: store ptr [[ADD_PTR29]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[SUB]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret void +// +void TestCount(int *__counted_by(len) ptr, unsigned len) { + ptr += foo(); + len = len - 4; +} + +// CHECK-LABEL: @TestRange( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[START:%.*]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END:%.*]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP13]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP24]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP27]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR27]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP28]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void TestRange(int *__ended_by(end) start, char *end) { + end = end; + start += 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c b/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c new file mode 100644 index 0000000000000..1768c6569c391 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c @@ -0,0 +1,55 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O2 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O2 +// CHECK_O0-LABEL: @foo( +// CHECK_O0: {{.*}}: +// CHECK_O0: [[ARRAY:%.*]] = alloca [10 x i32], align 16 +// CHECK_O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_O0: [[LOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_O0: call void @llvm.memset.p0.i64(ptr align 16 [[ARRAY]], i8 0, i64 40, i1 false) +// CHECK_O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARRAY]], i64 0, i64 0 +// CHECK_O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK_O0: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK_O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK_O0: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK_O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK_O0: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK_O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK_O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[LOCAL]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK_O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK_O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_O0: [[TMP3:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK_O0: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_O0: trap: +// CHECK_O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_O0: unreachable +// CHECK_O0: cont: +// CHECK_O0: [[TMP4:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK_O0: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK_O0: trap1: +// CHECK_O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK_O0: unreachable +// CHECK_O0: cont2: +// CHECK_O0: [[TMP5:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK_O0: ret i32 [[TMP5]] +// +// CHECK_O2-LABEL: @foo( +// CHECK_O2: {{.*}}: +// CHECK_O2: ret i32 0 +// +int foo(void) { + int array[10] = {0}; + int *ptr = array; + const int *local = ptr; + + return *local; +} diff --git a/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c b/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c new file mode 100644 index 0000000000000..ec24f6a858764 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +typedef struct { + unsigned int *__sized_by(length) data; + int length; +} Item; + +static unsigned int _oidRsa[] = { 0, 1, 2 }; +const Item oidRsa = { _oidRsa, sizeof(_oidRsa)}; + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_ITEM:%.*]], align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 @oidRsa, i64 16, i1 false) +// CHECK-NEXT: [[LENGTH:%.*]] = getelementptr inbounds [[STRUCT_ITEM]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LENGTH]], align 8 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds [[STRUCT_ITEM]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 3 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP5]], align 4 +// CHECK-NEXT: ret i32 [[TMP8]] +// +int main() { + return oidRsa.data[3]; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c b/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c new file mode 100644 index 0000000000000..2550ccf2c8c9e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c @@ -0,0 +1,129 @@ + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK,CHECKBS %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK,CHECKBS %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x c -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x c++ -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x objective-c -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x objective-c++ -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s + +#include + +/* Define to nothing in attribute-only mode */ +#ifndef __bidi_indexable +#define __bidi_indexable +#endif + +struct Foo { + void *dummy; + int buf[18]; +}; +_Static_assert(sizeof(struct Foo) == 80, "sizeof(struct Foo) should be 80"); + +int arr[2]; +int pad[2]; +struct Foo foo[8]; + +int *ptrSingle = __unsafe_forge_single(int *, 0); +// CHECK-LABEL: ptrSingle: +// CHECK: .quad 0 +// CHECK: .size ptrSingle, 8 + +/* XXX: won't work until __unsafe_forge_single knows the size it's forging +int *__indexable ptrIndexable = __unsafe_forge_single(int *, 0); +// TODO-CHECK-LABEL: ptrIndexable: +// TODO-CHECK: .zero 16 +// TODO-CHECK: .size ptrIndexable, 16 + */ + +int *__bidi_indexable ptrBidiIndexable = + __unsafe_forge_bidi_indexable(int *, arr + 2, 14); +// CHECKBS-LABEL: ptrBidiIndexable: +// CHECKBS: .quad arr+8 +// CHECKBS: .quad (arr+8)+14 +// CHECKBS: .quad arr+8 + +int *__bidi_indexable ptrBidiIndexable2 = __unsafe_forge_single(int *, &arr[0]); +// CHECKBS-LABEL: ptrBidiIndexable2: +// CHECKBS: .quad arr +// CHECKBS: .quad arr+4 +// CHECKBS: .quad arr + +int *__bidi_indexable ptrBidiIndexable3 = __unsafe_forge_single(int *, 0x1234); +// CHECKBS-LABEL: ptrBidiIndexable3: +// CHECKBS: .quad 4660 +// CHECKBS: .quad 4664 +// CHECKBS: .quad 4660 + +int *__bidi_indexable ptrBidiIndexable4 = __unsafe_forge_bidi_indexable(int *, 8000, 16); +// CHECKBS-LABEL: ptrBidiIndexable4: +// CHECKBS: .quad 8000 +// CHECKBS: .quad 8016 +// CHECKBS: .quad 8000 + +// (4 * 80) + 8 + 4 * 9 = 364 +int *__bidi_indexable ptrBidiIndexable5 = __unsafe_forge_bidi_indexable(int *, &foo[4].buf[9], 16); +// CHECKBS-LABEL: ptrBidiIndexable5: +// CHECKBS: .quad foo+364 +// CHECKBS: .quad (foo+364)+16 +// CHECKBS: .quad foo+364 +// CHECKBS: .size ptrBidiIndexable5, 24 + +#define FOO_PLUS_364 (&foo[4].buf[9]) +#define FOO_PLUS_364_LEN_16 __unsafe_forge_bidi_indexable(int *, FOO_PLUS_364, 16) +#define FOO_PLUS_380_LEN_0 (FOO_PLUS_364_LEN_16 + 4) +int *__bidi_indexable ptrBidiIndexable6 = + __unsafe_forge_bidi_indexable(int *, FOO_PLUS_380_LEN_0, 8) + 4; +// CHECKBS-LABEL: ptrBidiIndexable6: +// CHECKBS: .quad (foo+380)+16 +// CHECKBS: .quad (foo+380)+8 +// CHECKBS: .quad foo+380 +// CHECKBS: .size ptrBidiIndexable6, 24 + +int *ptrSingle2 = __unsafe_forge_bidi_indexable(int *, 0, 0); +// CHECKBS-LABEL: ptrSingle2: +// CHECKBS: .quad 0 +// CHECKBS: .size ptrSingle2, 8 + +int *ptrSingle3 = __unsafe_forge_bidi_indexable(int *, arr + 2, 4); +// CHECKBS-LABEL: ptrSingle3: +// CHECKBS: .quad arr+8 +// CHECKBS: .size ptrSingle3, 8 + +/* This is invalid; converting a pointer with a size of 3 to an int *__single + never works. */ +// int *ptrSingle4 = __unsafe_forge_bidi_indexable(int *, arr + 2, 3); + +int *ptrSingle5 = __unsafe_forge_single(int *, __unsafe_forge_bidi_indexable(int *, arr + 2, 3) + 4); +// CHECKBS-LABEL: ptrSingle5: +// CHECKBS: .quad arr+24 +// CHECKBS: .size ptrSingle5, 8 + +int *ptrSingle6 = __unsafe_forge_single(int *, &arr[0]); +// CHECK-LABEL: ptrSingle6: +// CHECK: .quad arr +// CHECK: .size ptrSingle6, 8 + +int *ptrSingle7 = __unsafe_forge_single(int *, 1337); +// CHECK-LABEL: ptrSingle7: +// CHECK: .quad 1337 +// CHECK: .size ptrSingle7, 8 + +int *ptrSingle8 = __unsafe_forge_single(int *, __unsafe_forge_single(int *, arr + 2)); +// CHECK-LABEL: ptrSingle8: +// CHECK: .quad arr+8 +// CHECK: .size ptrSingle8, 8 + +int *__terminated_by(5) ptrTerminated = __unsafe_forge_terminated_by(int *, arr, 5); +// CHECK-LABEL: ptrTerminated: +// CHECK: .quad arr +// CHECK: .size ptrTerminated, 8 + +char *__null_terminated ptrNt1 = __unsafe_forge_null_terminated(char *, __unsafe_forge_bidi_indexable(char *, arr+12, 10)); +// CHECKBS-LABEL: ptrNt1: +// CHECKBS: .quad arr+48 +// CHECKBS: .size ptrNt1, 8 + +char *__null_terminated ptrNt2 = __unsafe_forge_null_terminated(char *, __unsafe_forge_single(char *, arr+2)); +// CHECK-LABEL: ptrNt2: +// CHECK: .quad arr+8 +// CHECK: .size ptrNt2, 8 diff --git a/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c b/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c new file mode 100644 index 0000000000000..8b586af56a01a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +struct S { + int *__counted_by(count) ptr; + int count; +}; + +int assign_count_only_never_succeeds(struct S *s) { + s->count += 1; + s->ptr = s->ptr; +} + +int assign_count_only(struct S *s) { + s->count -= 1; + s->ptr = s->ptr; +} + +int assign_ptr(struct S *s) { + s->count = s->count - 1; + s->ptr = s->ptr + 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c b/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c new file mode 100644 index 0000000000000..18a22807f4b23 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -emit-llvm %s -o - | FileCheck %s + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: define dso_local i32 @fn_deref( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int fn_deref(int *__counted_by(count) p, int count) { + return *p; +} + +// CHECK-LABEL: define dso_local i32 @fn_subscript( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP1]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int fn_subscript(int *__counted_by(count) p, int count) { + return p[count - 1]; +} + +// CHECK-LABEL: define dso_local ptr @fn_assign( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 42 +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 42 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP2]] +// +int *fn_assign(int *__counted_by(count) p, int count) { + count = count - 42; + p = p + 42; + return p; +} + +struct bar { + int *__sized_by(size) q; + int size; +}; + +// CHECK-LABEL: define dso_local i32 @struct_deref( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int struct_deref(struct bar *b) { + return *b->q; +} + +// CHECK-LABEL: define dso_local i32 @struct_subscript( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[SIZE]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP3]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int struct_subscript(struct bar *b) { + return b->q[b->size - 1]; +} + +// CHECK-LABEL: define dso_local void @struct_assign( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP1]], 42 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE1:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[SUB]], ptr [[SIZE1]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 42 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q2:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[Q2]], align 8 +// CHECK-NEXT: ret void +// +void struct_assign(struct bar *b) { + b->size = b->size - 42; + b->q = b->q + 42; +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c new file mode 100644 index 0000000000000..525f1cfb74bed --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c @@ -0,0 +1,44 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// Executable version of byte-count-attr-fields-assign.c + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: define dso_local void @TestFail +// CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// +void TestFail() { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); + s.bp = &arr[1]; + s.l = n; // run-time trap - s.l cannot be bigger than element count of &arr[1] (9) and s.1 + 1 cannot be bigger than element count of arr (10) + s.bp2 = arr; +} + +// CHECK-LABEL: define dso_local void @TestOK +// CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 9 * sizeof(int); + s.bp2 = arr; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c new file mode 100644 index 0000000000000..8a6fd1b89679b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c @@ -0,0 +1,37 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9 * sizeof(int); // no trap +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap s.l > bounds of (s.bp) +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c new file mode 100644 index 0000000000000..fde205ae62ea9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c @@ -0,0 +1,220 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[N:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT5:%.*]], label [[TRAP4:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap4: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont5: +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BP]], align 8 +// CHECK-NEXT: [[BP2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[BP2]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[L]], align 8 +// CHECK-NEXT: store i32 40, ptr [[N]], align 4 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY15]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER16]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[ARRAYDECAY18:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER19:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY18]], i64 10 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER19]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load i32, ptr [[N]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR26]], ptr [[TMP22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB28]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB30]], ptr [[TMP24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP38:%.*]] = icmp sle i32 0, [[TMP21]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, [[HERE]] ], [ [[CMP38]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT41:%.*]], label [[TRAP40:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap40: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP21]], 4 +// CHECK-NEXT: [[CONV42:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR49]], ptr [[TMP26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB51]], ptr [[TMP27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB53]], ptr [[TMP28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST60:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST61:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR55]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB62:%.*]] = sub i64 [[SUB_PTR_LHS_CAST60]], [[SUB_PTR_RHS_CAST61]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP63:%.*]] = icmp sle i64 [[CONV42]], [[SUB_PTR_SUB62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP63]], label [[LAND_RHS65:%.*]], label [[LAND_END68:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs65: +// CHECK-NEXT: [[CMP66:%.*]] = icmp sle i64 0, [[CONV42]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END68]], {{!annotation ![0-9]+}} +// CHECK: land.end68: +// CHECK-NEXT: [[TMP29:%.*]] = phi i1 [ false, [[CONT41]] ], [ [[CMP66]], [[LAND_RHS65]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP29]], label [[CONT70:%.*]], label [[TRAP69:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap69: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont70: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[BP78:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR73]], ptr [[BP78]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP79]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR81:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-NEXT: [[BP286:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR81]], ptr [[BP286]], align 8 +// CHECK-NEXT: [[L87:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP21]], ptr [[L87]], align 8 +// CHECK-NEXT: ret void +// +void Test() { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); +here: + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap s.l > bounds of (s.bp) +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c new file mode 100644 index 0000000000000..2cf0176385a68 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c @@ -0,0 +1,22 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// Executable version of "count-attr-fields-assign-2.c" +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int main () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.l = n; // run-time trap - s.l cannot be bigger than element count of &arr[1] (9) and s.1 + 1 cannot be bigger than element count of arr (10) + s.bp2 = arr; + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c new file mode 100644 index 0000000000000..9eb4b6a9640ac --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c @@ -0,0 +1,45 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +// CHECK-O2-LABEL: define dso_local noundef i32 @foo +// CHECK-O2-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2:![0-9]+]] +// CHECK-O2-NEXT: unreachable, !annotation [[META2]] +// +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + + return s.bp2[9]; // trap : oob s.bp2[9] +} + +// CHECK-O2-LABEL: define dso_local i32 @bar +// CHECK-O2-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + + return s.bp2[8]; // ok +} +//. +// CHECK-O2: [[META2]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c new file mode 100644 index 0000000000000..79dad5d2435a7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c @@ -0,0 +1,280 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP43:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP57:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP74:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: br i1 true, label [[CONT5:%.*]], label [[TRAP4:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap4: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont5: +// CHECK-O0-NEXT: [[BP:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BP]], align 8 +// CHECK-O0-NEXT: [[BP2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[BP2]], align 8 +// CHECK-O0-NEXT: [[L:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: store i32 0, ptr [[L]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY15]], i64 10 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER16]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 1 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY18:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER19:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY18]], i64 10 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER19]], ptr [[TMP19]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: br i1 true, label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap20: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont21: +// CHECK-O0-NEXT: br i1 true, label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap22: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont23: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8 +// CHECK-O0-NEXT: [[BP31:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR26]], ptr [[BP31]], align 8 +// CHECK-O0-NEXT: [[L32:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: store i32 8, ptr [[L32]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-O0-NEXT: [[BP240:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BP240]], align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-O0-NEXT: br label [[FOR_COND:%.*]] +// CHECK-O0: for.cond: +// CHECK-O0-NEXT: [[TMP21:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[L41:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP22:%.*]] = load i32, ptr [[L41]], align 8 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP21]], [[TMP22]] +// CHECK-O0-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] +// CHECK-O0: for.body: +// CHECK-O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L44:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP24:%.*]] = load i32, ptr [[L44]], align 8 +// CHECK-O0-NEXT: [[BP45:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP25:%.*]] = load ptr, ptr [[BP45]], align 8 +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP24]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP25]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP27]], align 8 +// CHECK-O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP25]], ptr [[TMP28]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-O0-NEXT: [[TMP29:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP29]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR47]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-O0-NEXT: [[TMP30:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB49]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP30]], label [[CONT53:%.*]], label [[TRAP52:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap52: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont53: +// CHECK-O0-NEXT: [[TMP31:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB51]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP31]], label [[CONT55:%.*]], label [[TRAP54:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap54: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont55: +// CHECK-O0-NEXT: store i32 [[TMP23]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: br label [[FOR_INC:%.*]] +// CHECK-O0: for.inc: +// CHECK-O0-NEXT: [[TMP32:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP32]], 1 +// CHECK-O0-NEXT: store i32 [[INC]], ptr [[I]], align 4 +// CHECK-O0-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP5:![0-9]+]] +// CHECK-O0: for.end: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L58:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP33:%.*]] = load i32, ptr [[L58]], align 8 +// CHECK-O0-NEXT: [[BP259:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP34:%.*]] = load ptr, ptr [[BP259]], align 8 +// CHECK-O0-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP33]], 1 +// CHECK-O0-NEXT: [[IDX_EXT60:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR61:%.*]] = getelementptr inbounds i32, ptr [[TMP34]], i64 [[IDX_EXT60]] +// CHECK-O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP34]], ptr [[TMP35]], align 8 +// CHECK-O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR61]], ptr [[TMP36]], align 8 +// CHECK-O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP34]], ptr [[TMP37]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8 +// CHECK-O0-NEXT: [[ARRAYIDX64:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR63]], i64 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB66:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR65]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK-O0-NEXT: [[TMP38:%.*]] = icmp ult ptr [[ARRAYIDX64]], [[WIDE_PTR_UB66]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP38]], label [[CONT70:%.*]], label [[TRAP69:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap69: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont70: +// CHECK-O0-NEXT: [[TMP39:%.*]] = icmp uge ptr [[ARRAYIDX64]], [[WIDE_PTR_LB68]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP39]], label [[CONT72:%.*]], label [[TRAP71:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap71: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont72: +// CHECK-O0-NEXT: [[TMP40:%.*]] = load i32, ptr [[ARRAYIDX64]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L75:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP41:%.*]] = load i32, ptr [[L75]], align 8 +// CHECK-O0-NEXT: [[BP76:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP42:%.*]] = load ptr, ptr [[BP76]], align 8 +// CHECK-O0-NEXT: [[IDX_EXT77:%.*]] = sext i32 [[TMP41]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR78:%.*]] = getelementptr inbounds i32, ptr [[TMP42]], i64 [[IDX_EXT77]] +// CHECK-O0-NEXT: [[TMP43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP42]], ptr [[TMP43]], align 8 +// CHECK-O0-NEXT: [[TMP44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR78]], ptr [[TMP44]], align 8 +// CHECK-O0-NEXT: [[TMP45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP42]], ptr [[TMP45]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR80:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR79]], align 8 +// CHECK-O0-NEXT: [[ARRAYIDX81:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR80]], i64 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-O0-NEXT: [[TMP46:%.*]] = icmp ult ptr [[ARRAYIDX81]], [[WIDE_PTR_UB83]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP46]], label [[CONT87:%.*]], label [[TRAP86:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap86: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont87: +// CHECK-O0-NEXT: [[TMP47:%.*]] = icmp uge ptr [[ARRAYIDX81]], [[WIDE_PTR_LB85]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP47]], label [[CONT89:%.*]], label [[TRAP88:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap88: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont89: +// CHECK-O0-NEXT: [[TMP48:%.*]] = load i32, ptr [[ARRAYIDX81]], align 4 +// CHECK-O0-NEXT: [[ADD90:%.*]] = add nsw i32 [[TMP40]], [[TMP48]] +// CHECK-O0-NEXT: ret i32 [[ADD90]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[8]; // trap : oob s.bp[8] +} + + diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c new file mode 100644 index 0000000000000..cb006fffbc40b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 9; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.l = n; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +// CHECK: define noundef i32 @foo() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret i32 14 + +// CHECK: define noundef i32 @bar() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c new file mode 100644 index 0000000000000..6c82f43134128 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// Executable version of "count-attr-fields-assign.c" +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int main () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap : s.1 + 1 >= countof(arr or s.bp2) + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c new file mode 100644 index 0000000000000..44d1190b39941 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c @@ -0,0 +1,58 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.bp = &arr[9]; // trap: can't make the bounds smaller than s.l + s.l = s.l; + s.bp2 = s.bp2; + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.bp = &arr[0]; // no trap + s.l = s.l; + s.bp2 = s.bp2; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c new file mode 100644 index 0000000000000..8a732d32599dc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c @@ -0,0 +1,44 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = -1; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap : no negative len + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 0; // no trap + + return 0; +} + + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: entry: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c new file mode 100644 index 0000000000000..da6fc9c958bb0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c @@ -0,0 +1,50 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[9] + s.bp[7]; // trap : oobs +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 14 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c new file mode 100644 index 0000000000000..083d94fde788a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[8]; // trap : oob s.bp[8] +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; +} +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 14 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c new file mode 100644 index 0000000000000..ad998cdfffc97 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c @@ -0,0 +1,80 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[N:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 12, ptr [[N]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[N]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP6:%.*]] = icmp sle i32 0, [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP6]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN8:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[LEN8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[PTR16:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[PTR16]], align 8 +// CHECK-NEXT: ret void +// +void Test() { + int arr[10]; + struct Foo f; + int n = 12; + f.len = n; + f.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c new file mode 100644 index 0000000000000..629627db20cb0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c @@ -0,0 +1,33 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestOOB( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap{{.*}} +// CHECK-NEXT: unreachable +// +void TestOOB() { + int arr[10]; + struct Foo f; + int n = 12; + f.len = n; + f.ptr = arr; +} + +// CHECK-LABEL: @TestIB( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIB() { + int arr[10]; + struct Foo f; + f.len = 10; + f.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c new file mode 100644 index 0000000000000..70022a29cc93d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END33:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP17:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP17]], label [[LAND_RHS:%.*]], label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB20]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR23]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS30:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs30: +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i32 0, [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP31]], [[LAND_RHS30]] ] +// CHECK-NEXT: br label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.end33: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN34:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[LEN34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[PTR42:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[PTR42]], align 8 +// CHECK-NEXT: ret void +// +void Test(int len, int *__bidi_indexable ptr) { + struct Foo f; + f.len = len; + f.ptr = ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c new file mode 100644 index 0000000000000..8d307811b2c12 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c @@ -0,0 +1,61 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[16] = {0}; + int *__single tp = arr; + struct S s = {arr, arr, 0}; + s.bp = &arr[3]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp2[i] = i; + + int l = 10; + s.l = l; + s.bp = tp; // trap: s.l > 1 + s.bp2 = s.bp2; + + return 0; +} + +int bar () { + int arr[16] = {0}; + int *__single tp = arr; + struct S s = {arr, arr, 0}; + s.bp = &arr[3]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp2[i] = i; + + s.l = 1; + s.bp = tp; + s.bp2 = s.bp2; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c new file mode 100644 index 0000000000000..bdab0aec7ef10 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +// CHECK: define noundef i32 @foo() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret i32 14 + +// CHECK: define noundef i32 @bar() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c new file mode 100644 index 0000000000000..3415de11c1e60 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c @@ -0,0 +1,55 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +void side_effect(); + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + + side_effect(); + + s.bp = s.bp; + s.bp2 = &arr[8]; // trap : bound of &arr[8] < s.l + 1 + s.l = s.l; + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + + side_effect(); + + s.bp = s.bp; + s.bp2 = &arr[0]; // no trap + s.l = s.l; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c new file mode 100644 index 0000000000000..831aa1ef69353 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c @@ -0,0 +1,72 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__counted_by(len) ptr; + int *__counted_by(len - 1) ptr2; + int len; +}; + +// CHECK-LABEL: @TestPtrFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestPtrFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr2; // trap! bounds_of(s.ptr2) == 9 < 10 + s.ptr2 = arr; + s.len = 10; +} + +// CHECK-LABEL: @TestPtrOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestPtrOK() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr2; // ok: bounds_of(s.ptr2) == 9 <= 9 + s.ptr2 += 1; // bounds_of(s.ptr2 + 1) == 8 <= 8 + s.len = 9; +} + +// CHECK-LABEL: @TestPtrLBFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH18:%.*]] = getelementptr i8, ptr [[ARR]], i64 -8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH18]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP42_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP42_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont57: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestPtrLBFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr - 2; // trap! ptr < lb + s.ptr2 = arr; + s.len = 10; +} + +// CHECK-LABEL: @TestPtrUBFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestPtrUBFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr; + s.ptr2 = s.ptr2 + 10; // trap! ptr2 oob + s.len = 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c new file mode 100644 index 0000000000000..968323258d9d2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c @@ -0,0 +1,410 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include +struct S { + int *__counted_by(len) ptr; + int *__counted_by(len - 1) ptr2; + int len; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[PTR2]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[LEN16:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP15]], i32 0, i32 2 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[LEN16]], align 8 +// CHECK-NEXT: [[PTR217:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[PTR217]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP15]], 1 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[ARRAYDECAY19:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER20:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY19]], i64 10 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER20]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY19]], ptr [[TMP22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_UB30]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END54:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_LB33]], [[WIDE_PTR_PTR36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP41]], label [[LAND_RHS:%.*]], label [[LAND_END54]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB44]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR47]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP52:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP52]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs53: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP23:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LAND_END54]], {{!annotation ![0-9]+}} +// CHECK: land.end54: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP23]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT56:%.*]], label [[TRAP55:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap55: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont56: +// CHECK-NEXT: br i1 true, label [[CONT58:%.*]], label [[TRAP57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap57: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont58: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP59]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR61:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB63:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR64:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB65:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR64]], align 8 +// CHECK-NEXT: [[PTR66:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR61]], ptr [[PTR66]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP67]], ptr align 8 [[AGG_TEMP18]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR68:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR69:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR68]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB71:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB73:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR72]], align 8 +// CHECK-NEXT: [[PTR274:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR69]], ptr [[PTR274]], align 8 +// CHECK-NEXT: [[LEN75:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN75]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; +here: + s.ptr = s.ptr2; + s.ptr2 = arr; + s.len = 10; + return 0; +} + +// CHECK-LABEL: @Bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[PTR2]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[LEN17:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[LEN17]], align 8 +// CHECK-NEXT: [[PTR18:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[PTR18]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP21]], i64 -2 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP24]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 2 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[ARRAYDECAY21:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER22:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY21]], i64 10 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY21]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER22]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY21]], ptr [[TMP31]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_UB32]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP43]], label [[LAND_RHS:%.*]], label [[LAND_END56]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR49]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP54]], label [[LAND_RHS55:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs55: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP32:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS55]] ] +// CHECK-NEXT: br label [[LAND_END56]], {{!annotation ![0-9]+}} +// CHECK: land.end56: +// CHECK-NEXT: [[TMP33:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP32]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP33]], label [[CONT58:%.*]], label [[TRAP57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap57: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont58: +// CHECK-NEXT: br i1 true, label [[CONT60:%.*]], label [[TRAP59:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap59: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont60: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8 +// CHECK-NEXT: [[PTR68:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR63]], ptr [[PTR68]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP20]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: [[PTR276:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR71]], ptr [[PTR276]], align 8 +// CHECK-NEXT: [[LEN77:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN77]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Bar() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; +here: + s.ptr = s.ptr - 2; + s.ptr2 = arr; + s.len = 10; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c new file mode 100644 index 0000000000000..7d8d0f0d587e0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c @@ -0,0 +1,130 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestInitFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestInitFail1() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; // trap: +} + +// CHECK-LABEL: @TestInitFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestInitFail2() { + int arr[9] = {0}; + int *ptr = arr; + int len = -1; + struct S s = {len, ptr}; // trap: +} + +struct SU { + unsigned long len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestUInitFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestUInitFail() { + int arr[9] = {0}; + int *ptr = arr; + long len = -1; + struct SU s = {len, ptr}; // trap: +} + +// CHECK-LABEL: @TestUInitOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestUInitOK() { + int arr[9] = {0}; + int *ptr = arr; + long len = 8; + struct SU s = {len, ptr}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; +} + +// CHECK-LABEL: @TestAccessOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestAccessOK() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[8]; +} + +// CHECK-LABEL: @TestAccessFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestAccessFail() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[9]; +} + +// CHECK-LABEL: @TestAccessFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 36, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 16 dereferenceable(36) [[ARR]], i8 0, i64 36, i1 false) +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT47:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont47: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 36, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestAccessFail2() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[-1]; +} + +// CHECK-LABEL: @TestAccessFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestAccessFail3() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)*(s.ptr + 9); +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c new file mode 100644 index 0000000000000..25ecea3e2b152 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 36, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END27:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.lhs.true: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP25:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP25]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs26: +// CHECK-O0-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end: +// CHECK-O0-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS26]] ] +// CHECK-O0-NEXT: br label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end27: +// CHECK-O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[PTR28:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR28]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c new file mode 100644 index 0000000000000..6b2891146e2bc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c @@ -0,0 +1,78 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[10]; + int len = 11; + struct S s = {.ptr = arr, .len = len}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int len = 0; + struct S s = {.len = len}; +} + +struct S2 { + int len; + int *__counted_by(len+1) ptr; +}; + +// CHECK-LABEL: @Test2OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test2OK() { + int len = -1; + struct S2 s = {.len = len, .ptr = 0}; +} + +// CHECK-LABEL: @Test2Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test2Fail() { + int len = 0; + struct S2 s = {.len = len, .ptr = 0}; +} + +struct S3 { + int *__counted_by(len+2) ptr; + int len; +}; + + +// CHECK-LABEL: @Test3OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test3OK() { + int a[2]; + struct S3 s = {.len = 0, .ptr = a}; +} + +// CHECK-LABEL: @Test3Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test3Fail() { + int a; + struct S3 s = {.len = 0, .ptr = &a}; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c new file mode 100644 index 0000000000000..bab6bf8aa9190 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// ... +// ... + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 36, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END27:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.lhs.true: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP25:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP25]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs26: +// CHECK-O0-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end: +// CHECK-O0-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS26]] ] +// CHECK-O0-NEXT: br label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end27: +// CHECK-O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[PTR28:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR28]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {.ptr = ptr, .len = 10}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c new file mode 100644 index 0000000000000..90158e47ff576 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c @@ -0,0 +1,27 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int main() { + int arr[9] = {0}; + struct S s = {8, arr}; // in bounds + + return s.ptr[7]; +} + +// CHECK: ret i32 0 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c new file mode 100644 index 0000000000000..7ddd56cbdc6da --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c @@ -0,0 +1,77 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int cnt = 1; + struct S s = {cnt}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int len = 0; + struct S s = {len}; +} + +struct S2 { + int len; + int *__counted_by(len+1) ptr; +}; + +// CHECK-LABEL: @Test2OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test2OK() { + int len = -1; + struct S2 s = {len}; +} + +// CHECK-LABEL: @Test2Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test2Fail() { + int len = 0; + struct S2 s = {len}; +} + +struct S3 { + int *__counted_by(len+2) ptr; + int len; +}; + + +// CHECK-LABEL: @Test3OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test3OK() { + int a[2]; + struct S3 s = {a}; +} + +// CHECK-LABEL: @Test3Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test3Fail() { + int a; + struct S3 s = {&a}; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c new file mode 100644 index 0000000000000..6e52931e3d9a7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[VAR_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: store i32 [[VAR:%.*]], ptr [[VAR_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[VAR_ADDR]], align 4 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 [[TMP0]], ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr null, ptr [[PTR]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test(int var) { + struct S s = {var}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c new file mode 100644 index 0000000000000..5190950ee6635 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c @@ -0,0 +1,43 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int foo() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {8, &arr[2]}, 0}; // trap + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {7, &arr[2]}, 0}; // no trap + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c new file mode 100644 index 0000000000000..f02bc490344b6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int main() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {7, &arr[2]}, 0}; // all in bounds + + return 0; +} + +// CHECK: ret i32 0 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c new file mode 100644 index 0000000000000..c6996da3e3dc0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c @@ -0,0 +1,44 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; +}; + +int foo() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; // ok + // XXX: We shouldn't check again for s. Verify. + struct T t = {10, ptr}; // trap : len > boundof(ptr) + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; // ok + // XXX: We shouldn't check again for s. Verify. + struct T t = {9, arr}; // ok + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c new file mode 100644 index 0000000000000..0728e5492c510 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int foo() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; // trap: len > boundof(ptr) + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; // ok + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c new file mode 100644 index 0000000000000..f70f11d9f800b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +int foo(int *__counted_by(*out_len)* out_buf, int *out_len) { + int arr[10]; + int n = 11; + *out_len = n; // trap : 11 > boundof(arr) + *out_buf = arr; + return 0; +} + +int bar(int *__counted_by(*out_len)* out_buf, int *out_len) { + int arr[10]; + *out_len = 9; + *out_buf = arr; + return 0; +} + +int main() { + int len; + int *__counted_by(len) buf; + foo(&buf, &len); + + return buf[len-1]; +} + +// CHECK: define noundef i32 @foo +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar +// CHECK: {{.*}}: +// CHECK: ret i32 0 + +// CHECK: define noundef i32 @main() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c new file mode 100644 index 0000000000000..649b98d894afc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include +int foo () { + int arr[10]; + int n = 11; + int len = n; // trap : 11 > boundsof(arr) + int *__counted_by(len) buf = arr; + + return 0; +} + +int bar () { + int arr[10]; + int len = 10; + int *__counted_by(len) buf = arr; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c new file mode 100644 index 0000000000000..67cab3246ea96 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c @@ -0,0 +1,200 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +// CHECK-LABEL: @idx_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i8 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_char(int *__counted_by(n) dst, unsigned n, char idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_unsigned_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_unsigned_char(int *__counted_by(n) dst, unsigned n, unsigned char idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_short_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i16 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_short_int(int *__counted_by(n) dst, unsigned n, short int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_short_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i16 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_short_unsigned(int *__counted_by(n) dst, unsigned n, short unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_int(int *__counted_by(n) dst, unsigned n, int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_unsigned(int *__counted_by(n) dst, unsigned n, unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_int(int *__counted_by(n) dst, unsigned n, long int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_unsigned(int *__counted_by(n) dst, unsigned n, long unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_unsigned_counted_by_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_unsigned_counted_by_int(int *__counted_by(n) dst, int n, long unsigned idx) { + // + dst[idx] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c b/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c new file mode 100644 index 0000000000000..662bb9b00ed8e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +#include + +int main() { + int len; + int *__counted_by(len) ptr1; + int len2; + int *__counted_by(len2) ptr2; + unsigned long long diff = (unsigned long long)(ptr1 - ptr2); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-return.c b/clang/test/BoundsSafety/CodeGen/count-return.c new file mode 100644 index 0000000000000..1c3b516c15b31 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-return.c @@ -0,0 +1,134 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +int *__sized_by(len) alloc(int len); + +// CHECK-O0-LABEL: @Test1( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[BUFAUTO:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BUFBOUND:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 16, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @alloc(i32 noundef [[TMP0]]) +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[CALL1:%.*]] = call ptr @alloc(i32 noundef 40) +// CHECK-O0-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 40 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUFBOUND]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp ult ptr [[TMP7]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP9:%.*]] = icmp uge ptr [[TMP7]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP9]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP10:%.*]] = load i32, ptr [[TMP7]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP10]] +// +// CHECK-O2-LABEL: @Test1( +// CHECK-O2-NEXT: cont3: +// CHECK-O2-NEXT: [[CALL:%.*]] = tail call ptr @alloc(i32 noundef 16) #[[ATTR4:[0-9]+]] +// CHECK-O2-NEXT: [[CALL1:%.*]] = tail call ptr @alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 36 +// CHECK-O2-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret i32 [[TMP1]] +// +int Test1() { + int len = 16; + int *bufAuto = alloc(len); + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); + return bufBound[9]; +} + +// CHECK-O0-LABEL: @Test2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[BUFAUTO:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BUFBOUND:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 16, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @alloc(i32 noundef [[TMP0]]) +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[CALL1:%.*]] = call ptr @alloc(i32 noundef 40) +// CHECK-O0-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 40 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUFBOUND]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp ult ptr [[TMP7]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP9:%.*]] = icmp uge ptr [[TMP7]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP9]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP10:%.*]] = load i32, ptr [[TMP7]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP10]] +// +// CHECK-O2-LABEL: @Test2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[CALL:%.*]] = tail call ptr @alloc(i32 noundef 16) #[[ATTR4]] +// CHECK-O2-NEXT: [[CALL1:%.*]] = tail call ptr @alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int Test2() { + int len = 16; + int *bufAuto = alloc(len); + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); + return bufBound[10]; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c new file mode 100644 index 0000000000000..065c868faea4b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c @@ -0,0 +1,294 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s --check-prefix WITHOUT +#include + + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP14]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP16]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[DEC:%.*]] = add i32 [[TMP17]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// WITHOUT-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP0]], align 4 +// WITHOUT-NEXT: ret void +// +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; +#ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; +#endif +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[SUB]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP15]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR34]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// WITHOUT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// WITHOUT-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// WITHOUT-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// WITHOUT-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// WITHOUT-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// WITHOUT-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// WITHOUT-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// WITHOUT-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// WITHOUT-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// WITHOUT-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// WITHOUT-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR]], align 4 +// WITHOUT-NEXT: ret void +// +void bar(int *__counted_by(count) x, int count) { +#ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; +#endif + *(x = x+1) = 0; +} + + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c new file mode 100644 index 0000000000000..13f9b41982d9c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c @@ -0,0 +1,108 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH -O2 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update -O2 %s -o - | FileCheck %s --check-prefix WITHOUT +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef writeonly [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR_IDX:%.*]] = shl nuw nsw i64 [[IDX_EXT]], 2, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[ADD_PTR_IDX]], !annotation [[META2]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META2]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[X]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP15_NOT]], [[CMP_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[COUNT]], -1, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[ADD_PTR_IDX]], -4, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp slt i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[X]], align 4, !tbaa [[TBAA7:![0-9]+]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr nocapture noundef writeonly [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: store i32 0, ptr [[X]], align 4, !tbaa [[TBAA2:![0-9]+]] +// WITHOUT-NEXT: ret void +// +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; + #endif +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef writeonly [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR_IDX:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META5]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[ADD_PTR_IDX]], !annotation [[META2]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META2]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[X]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP15_NOT]], [[CMP_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB:%.*]] = add nsw i32 [[COUNT]], -1, !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[ADD_PTR_IDX]], -4, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP29:%.*]] = icmp sgt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP29]], [[CMP26]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA7]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr nocapture noundef writeonly [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void bar(int *__counted_by(count) x, int count) { + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; + #endif + *(x = x+1) = 0; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{[[META4:![0-9]+]]} +// CHECK: [[META4]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META5]] = !{!"bounds-safety-generic", [[META6:![0-9]+]]} +// CHECK: [[META6]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META11]] = !{[[META6]]} +//. +// WITHOUT: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// WITHOUT: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// WITHOUT: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// WITHOUT: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c new file mode 100644 index 0000000000000..90e3f519cc42e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c @@ -0,0 +1,588 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-init-list -O0 -triple arm64 -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://114446928 +// CHECK-LABEL: define dso_local ptr @foo +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int * __counted_by_or_null(len) foo(int * __bidi_indexable p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @foo_assign +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[SIZE]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END39]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR19]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS36:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs36: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP37]], [[LAND_RHS36]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP2]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END39]], !annotation [[META2]] +// CHECK: land.end39: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[P2]], align 8 +// CHECK-NEXT: ret void +// +void foo_assign(int * __bidi_indexable p, int len) { + int size = len; + int * __counted_by_or_null(size) p2 = p; +} + +// CHECK-LABEL: define dso_local void @bar +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int * __bidi_indexable bar(int * __counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @bar_assign +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +void bar_assign(int * __counted_by_or_null(len) p, int len) { + int * __bidi_indexable p2 = p; +} + +// CHECK-LABEL: define dso_local void @ptr_oob +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[X:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[X]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[X]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[X]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END34:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END34]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 4, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label [[LAND_RHS33:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs33: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS33]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP13]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END34]], !annotation [[META2]] +// CHECK: land.end34: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP14]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[Q]], align 8 +// CHECK-NEXT: ret void +// +void ptr_oob(void) { + int x; + int *__bidi_indexable p = &x; + --p; + int *__counted_by_or_null(4) q = p; +} + +// CHECK-LABEL: define dso_local void @null_count_neg +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[P]], i8 0, i64 24, i1 false) +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[Q]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 -42, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP0]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.end35: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: store i32 -42, ptr [[COUNT]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_neg(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = -42; + q = p; + return q; +} + +// CHECK-LABEL: define dso_local void @null_count_too_big +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[P]], i8 0, i64 24, i1 false) +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4]] +// CHECK-NEXT: store ptr null, ptr [[Q]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP0]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.end35: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[COUNT]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_too_big(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = 42; + q = p; + return q; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c new file mode 100644 index 0000000000000..65eaccbcaf061 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c @@ -0,0 +1,134 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-init-list -O2 -triple arm64 -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://114446928 +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int * __counted_by_or_null(len) foo(int * __bidi_indexable p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @foo_assign( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP37:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP37]], [[CMP34]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void foo_assign(int * __bidi_indexable p, int len) { + int size = len; + int * __counted_by_or_null(size) p2 = p; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ADD_PTR_SINK:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: store ptr [[P]], ptr [[AGG_RESULT]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-NEXT: store ptr [[ADD_PTR_SINK]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: ret void +// +int * __bidi_indexable bar(int * __counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @bar_assign( +// CHECK-SAME: ptr nocapture noundef readnone [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void bar_assign(int * __counted_by_or_null(len) p, int len) { + int * __bidi_indexable p2 = p; +} + +// CHECK-LABEL: define dso_local void @ptr_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[TRAP:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// +void ptr_oob(void) { + int x; + int *__bidi_indexable p = &x; + --p; + int *__counted_by_or_null(4) q = p; +} + +// CHECK-LABEL: define dso_local void @null_count_neg( +// CHECK-SAME: ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_neg(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = -42; + q = p; + return q; +} + +// CHECK-LABEL: define dso_local void @null_count_too_big( +// CHECK-SAME: ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_too_big(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = 42; + q = p; + return q; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// CHECK: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META9]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c new file mode 100644 index 0000000000000..ac778feef9964 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c @@ -0,0 +1,467 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64 -emit-llvm -Wno-bounds-safety-init-list %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: define dso_local void @to_bidi +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END15:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label [[LAND_RHS:%.*]], label [[LAND_END15]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB9]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP10:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP10]], label [[LAND_RHS12:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs12: +// CHECK-NEXT: [[CMP13:%.*]] = icmp sle i64 0, [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP13]], [[LAND_RHS12]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP11]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END15]], !annotation [[META2]] +// CHECK: land.end15: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP12]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[TMP15]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP17]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP16]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP23]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_literal_count +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END10:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label [[LAND_RHS:%.*]], label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 -1, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label [[LAND_RHS9:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs9: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS9]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.end10: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 -1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_literal_count(int * arg) { + int * __counted_by_or_null(-1) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_const_count +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END10:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label [[LAND_RHS:%.*]], label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 -1, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label [[LAND_RHS9:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs9: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS9]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.end10: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 -1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @back_and_forth_to_bidi +// CHECK-SAME: (ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: store ptr null, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[ARG]], i64 24, i1 false) +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END38:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END38]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS35:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs35: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP36]], [[LAND_RHS35]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END38]], !annotation [[META2]] +// CHECK: land.end38: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP9]], ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP13]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL46:%.*]], label [[BOUNDSCHECK_NULL49:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull46: +// CHECK-NEXT: [[IDX_EXT47:%.*]] = sext i32 [[TMP14]] to i64 +// CHECK-NEXT: [[ADD_PTR48:%.*]] = getelementptr inbounds i32, ptr [[TMP13]], i64 [[IDX_EXT47]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR48]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT50:%.*]] +// CHECK: boundscheck.null49: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT50]] +// CHECK: boundscheck.cont50: +// CHECK-NEXT: ret void +// +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c new file mode 100644 index 0000000000000..de911fe6dabeb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c @@ -0,0 +1,78 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + +// RUN: %clang_cc1 -fbounds-safety -O2 -triple arm64 -emit-llvm -Wno-bounds-safety-init-list %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: define dso_local void @to_bidi +// CHECK-SAME: (ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef readnone [[ARG:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_literal_count +// CHECK-SAME: (ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef readnone [[ARG:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP_CRITEDGE:%.*]], !annotation [[META2]] +// CHECK: trap.critedge: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_literal_count(int * arg) { + int * __counted_by_or_null(-1) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_const_count +// CHECK-SAME: (ptr dead_on_unwind noalias nocapture writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef readnone [[ARG:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP_CRITEDGE:%.*]], !annotation [[META2]] +// CHECK: trap.critedge: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @back_and_forth_to_bidi +// CHECK-SAME: (ptr nocapture noundef writeonly [[ARG:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[ARG]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c new file mode 100644 index 0000000000000..2137ce205e0a2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c @@ -0,0 +1,151 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by_or_null(len) buf; + int len; +}; + +int *__counted_by_or_null(cnt) my_alloc_int(int cnt); + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca [[STRUCT_PACKET:%.*]], align 8 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 9, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_alloc_int(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[CALL]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP4]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], {{!annotation ![0-9]+}} +// CHECK: lor.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP8]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.end35: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP9]], [[LOR_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[BUF43:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[BUF43]], align 8 +// CHECK-NEXT: [[LEN44:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 10, ptr [[LEN44]], align 8 +// CHECK-NEXT: ret void +// +void Foo(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c b/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c new file mode 100644 index 0000000000000..17bb70808b02f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c @@ -0,0 +1,100 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned data_const_global_count __unsafe_late_const; + +// CHECK-LABEL: @get_const_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @data_const_global_count, align 4 +// CHECK-NEXT: ret i32 [[TMP0]] +// +__attribute__((const)) int get_const_count() { + return data_const_global_count; +} + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNTED_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNTED_PTR:%.*]], ptr [[COUNTED_PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_const_count() #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END33:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB20]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR23]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS30:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs30: +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP31]], [[LAND_RHS30]] ] +// CHECK-NEXT: br label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.end33: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP0]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[COUNTED_PTR_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test(int *__counted_by(get_const_count()) counted_ptr, int *__bidi_indexable new_ptr) { + counted_ptr = new_ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c new file mode 100644 index 0000000000000..0a216f29a33b9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +__attribute__((const)) static int get_const_count() { + return 4; +} + +// CHECK-LABEL: @test_assign_const_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_assign_const_count_ok(int *__counted_by(get_const_count()) counted_ptr) { + int arr[4]; + counted_ptr = arr; +} + +// CHECK-LABEL: @test_assign_const_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_assign_const_count_fail(int *__counted_by(get_const_count()) counted_ptr) { + int arr[3]; + counted_ptr = arr; +} + +__attribute__((const)) static int get_const_size() { + return 10; +} + +// CHECK-LABEL: @test_assign_const_size_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_assign_const_size_ok(void *__sized_by(get_const_size()) sized_ptr) { + char arr[10]; + sized_ptr = arr; +} + +// CHECK-LABEL: @test_assign_const_size_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_assign_const_size_fail(void *__sized_by(get_const_size()) sized_ptr) { + char arr[9]; + sized_ptr = arr; +} + +// flexible array member +struct struct_flex { + long dummy; + long fam[__counted_by(get_const_count())]; +}; + +// CHECK-LABEL: @test_flexible_assign_const_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flexible_assign_const_count_ok(struct struct_flex *flex) { + char arr[sizeof(long) * 5]; + flex = (struct struct_flex *)arr; +} + +// CHECK-LABEL: @test_flexible_assign_const_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_flexible_assign_const_count_fail(struct struct_flex *flex) { + char arr[sizeof(long) * 4]; + flex = (struct struct_flex *)arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c b/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c new file mode 100644 index 0000000000000..1e3b92555f24b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c @@ -0,0 +1,91 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s +#include + +int glen; +int *__counted_by(glen) gptr; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG:%.*]], ptr [[ARG_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[ARG]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP0]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 0, ptr @glen, align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr @gptr, align 8 +// CHECK-NEXT: ret void +// +void test(int *__bidi_indexable arg) { + glen = 0; + gptr = arg; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c b/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c new file mode 100644 index 0000000000000..3196688b2771c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c @@ -0,0 +1,112 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// First make sure the previously buggy diagnostic is present +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s + + +// Now make sure the codegen is as expected and identical with and without the new bounds checks +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64-apple-iphoneos -emit-llvm -fno-bounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count %s -o - | FileCheck %s + + +#include +// CHECK-LABEL: define dso_local void @test_cb_const_inc( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 3 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 3, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META2]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP13]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test_cb_const_inc(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + ++p; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c new file mode 100644 index 0000000000000..5626856cc145c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c @@ -0,0 +1,66 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct Item { + int *__counted_by(len) buf; + unsigned len; +}; + +// CHECK-LABEL: @TestIncOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len--; +} + +// CHECK-LABEL: @TestIncOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK2() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = 9; +} + +// CHECK-LABEL: @TestIncOK3( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK3() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf = arr + 1; + s.len--; +} + +// CHECK-LABEL: @TestIncTrap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIncTrap() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = 10; +} + +// CHECK-LABEL: @TestIncTrap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIncTrap2() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = s.len; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c b/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c new file mode 100644 index 0000000000000..96cf8ca23f094 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c @@ -0,0 +1,114 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct Item { + int *__counted_by(len) buf; + unsigned len; +}; + +// CHECK-LABEL: @TestInc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_ITEM:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_ITEM]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds [[STRUCT_ITEM]], ptr [[TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP16]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP27]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP18]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add i32 [[TMP19]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[LEN1]], align 8 +// CHECK-NEXT: ret void +// +void TestInc(struct Item *s) { + s->buf++; + s->len--; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c new file mode 100644 index 0000000000000..65613e23bdde3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c @@ -0,0 +1,204 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__counted_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 2) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 0) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// rdar://118117905 +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[P]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[LEN]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_9_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_9_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_LHS_TRUE:%.*]], !annotation [[META3]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_17_0_P_SROA_IDX]], align 8, !tbaa [[TBAA5:!.+]] +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_17_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[CONT:%.*]], label [[LOR_RHS:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_9_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META3]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META3]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP54]], [[CMP51]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT]], label [[TRAP]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[AGG_TEMP_SROA_0_0_COPYLOAD]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[LEN]], 2 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[TOBOOL_NOT]], [[SPEC_SELECT]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @bar(ptr noundef [[OUT]], ptr noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META13:![0-9]+]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META13]] +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[P]], ptr noundef nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA5]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[CMP_NOT83:%.*]] = icmp slt i32 [[TMP1]], 0, !annotation [[META3]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT83]], i1 false, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META14:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: land.rhs: +// CHECK-NEXT: br i1 [[DOTNOT]], label [[LOR_RHS:%.*]], label [[CONT60:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sge i32 [[TMP1]], [[LEN]], !annotation [[META3]] +// CHECK-NEXT: [[CMP57:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP57]], [[CMP54]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT60]], label [[TRAP]], !annotation [[META3]] +// CHECK: cont60: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META3]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA5]] = !{[[META6:![0-9]+]], [[META6]], i64 0} +// CHECK: [[META6]] = !{!"any pointer", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META11]] = !{!"bounds-safety-generic", [[META12:![0-9]+]]} +// CHECK: [[META12]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META13]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META14]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c new file mode 100644 index 0000000000000..856dd2366d7ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c @@ -0,0 +1,862 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__counted_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2:![0-9]+]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 0) +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP13]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP14]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP9]]) +// CHECK-NEXT: ret void +// +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef -1) +// CHECK-NEXT: ret void +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP9]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP10]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP5]]) +// CHECK-NEXT: ret void +// +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP4]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP5]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB2:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_PTR]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END29:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB6]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP13:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP13]], label [[LAND_RHS:%.*]], label [[LAND_END29]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER15:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER15]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB17]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR19]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP24:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs26: +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP27]], [[LAND_RHS26]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP14]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END29]], !annotation [[META2]] +// CHECK: land.end29: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP15]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef [[TMP0]], i32 noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[OUT]], ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP0]], ptr noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP65:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr ptr, ptr [[P]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[COUNT]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4]], label [[TRAP3:%.*]], !annotation [[META6]] +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR7]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL12:%.*]], label [[CONT16:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull12: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_UB9]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT14:%.*]], label [[TRAP13:%.*]], !annotation [[META5]] +// CHECK: trap13: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont14: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_LB11]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT16]], label [[TRAP15:%.*]], !annotation [[META6]] +// CHECK: trap15: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont16: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]], ptr noundef [[WIDE_PTR_PTR7]]) +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL18:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull18: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_UB28]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END62:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP39:%.*]] = icmp ule ptr [[WIDE_PTR_LB31]], [[WIDE_PTR_PTR34]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS:%.*]], label [[LAND_END62]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR42]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP23]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB49]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR52]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP57:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP57]], label [[LAND_RHS59:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs59: +// CHECK-NEXT: [[CMP60:%.*]] = icmp sle i32 0, [[TMP23]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP60]], [[LAND_RHS59]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP24]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END62]], !annotation [[META2]] +// CHECK: land.end62: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP25]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label [[CONT64:%.*]], label [[TRAP63:%.*]], !annotation [[META2]] +// CHECK: trap63: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont64: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP65]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR66:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR67:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR66]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR68:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB69:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR68]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB71:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR70]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR67]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP23]], ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP29:%.*]] = icmp ne ptr [[TMP27]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP29]], label [[BOUNDSCHECK_NOTNULL73:%.*]], label [[BOUNDSCHECK_NULL76:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull73: +// CHECK-NEXT: [[IDX_EXT74:%.*]] = sext i32 [[TMP28]] to i64 +// CHECK-NEXT: [[ADD_PTR75:%.*]] = getelementptr inbounds i32, ptr [[TMP27]], i64 [[IDX_EXT74]] +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR75]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP32]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT77:%.*]] +// CHECK: boundscheck.null76: +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP35]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT77]] +// CHECK: boundscheck.cont77: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR79]] +// +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c new file mode 100644 index 0000000000000..e587db9273acd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c @@ -0,0 +1,133 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +struct PacketUnsigned { + int *__counted_by(len) buf; + unsigned long long len; +}; + +int *__counted_by(cnt) my_alloc_int(int cnt); +void *__sized_by(siz) my_alloc(int siz); +int *__sized_by(siz) my_alloc_int_siz(int siz); + + +// CHECK-LABEL: @TestCountIntFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef 9) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountIntFail(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestCountNegFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountNegFail(void) { + struct Packet p; + int siz = -1; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestUCountNegFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestUCountNegFail(void) { + struct PacketUnsigned p; + int siz = -1; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestCountNegRetFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountNegRetFail(void) { + int *local_p = my_alloc_int(-1); //rdar://80808704 + (void)*local_p; +} + +// CHECK-LABEL: @TestCountIntOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef 10) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestCountIntOK(void) { + struct Packet p; + int siz = 10; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestSizeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i32 noundef 39) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestSizeFail(void) { + struct Packet p; + int siz = 10 * sizeof(int) - 1; + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestSizeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestSizeOK(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestIntSizeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int_siz(i32 noundef 39) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIntSizeFail(void) { + struct Packet p; + int siz = 10 * sizeof(int) - 1; + p.buf = my_alloc_int_siz(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestIntSizeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int_siz(i32 noundef 40) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestIntSizeOK(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc_int_siz(siz); + p.len = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c new file mode 100644 index 0000000000000..fd738f2b975a7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c @@ -0,0 +1,123 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +int *__counted_by(cnt) my_alloc_int(int cnt); + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca [[STRUCT_PACKET:%.*]], align 8 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 9, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_alloc_int(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[BUF36:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[BUF36]], align 8 +// CHECK-NEXT: [[LEN37:%.*]] = getelementptr inbounds [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 10, ptr [[LEN37]], align 8 +// CHECK-NEXT: ret void +// +void Foo(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c b/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c new file mode 100644 index 0000000000000..bf564bfd33882 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fbounds-safety -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "__bounds_safety::wide_ptr + +#include + +void f() { + int arr[10]; + int *ptr = &arr[0]; +} + +int test_attrs(int *__counted_by(num_elements - 1) ptr_counted_by, + int *__sized_by(num_elements * 4) ptr_sized_by, int *end, + int *__ended_by(end) ptr_ended_by, int num_elements) { + return 0; +} + +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::counted_by::num_elements - 1" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::sized_by::num_elements * 4" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::dynamic_range::ptr_ended_by::" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::dynamic_range::::end" diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c new file mode 100644 index 0000000000000..3543f3c3fa2f0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c @@ -0,0 +1,124 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct buf { + void *__sized_by(len) data; + long len; +}; + +struct buf *slice; + +// CHECK-LABEL: @f( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN2:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[LEN2]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @slice, align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_BUF:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[LEN]], align 8 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds [[STRUCT_BUF]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[LEN2]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP6]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR23]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB25]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB27]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS36:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs36: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP37]], [[LAND_RHS36]] ] +// CHECK-NEXT: br label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.end39: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP10]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[X]], align 8 +// CHECK-NEXT: ret void +// +void f(void) { + int len2; + void *__sized_by(len2) x = slice->data; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c new file mode 100644 index 0000000000000..609822ee7f9f3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c @@ -0,0 +1,76 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +struct S { int *__counted_by(len) ptr; int len; }; +int get_len(int *__counted_by(max_len) ptr, int max_len); + +void foo(void) { + struct S s; + int arr[10] = {0}; + int *ptr = arr; + s.len = get_len(ptr, 10); + s.ptr = (ptr); +} + +// CHECK-LABEL: bar +void bar(void) { + struct S s; + int arr[10] = {0}; + void *ptr = arr; +assignment: + s.len = 10; + s.ptr = (int *)ptr; +// CHECK-LABEL: assignment +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} 's' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'ptr' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .ptr +// CHECK: | | `-DeclRefExpr {{.+}} 's' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c new file mode 100644 index 0000000000000..be5eb6bcaec92 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c @@ -0,0 +1,278 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @for_cond_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP90:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP93:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP1]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[WIDE_PTR_PTR]], align 1 +// CHECK-NEXT: store i8 [[TMP7]], ptr [[VAL]], align 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP4:%.*]] = icmp sge i64 [[TMP9]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP4]]) +// CHECK-NEXT: [[ADD_PTR5:%.*]] = getelementptr inbounds i8, ptr [[TMP8]], i64 [[TMP9]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR5]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP14]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i64 [[TMP22]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB15]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_LB19]], [[WIDE_PTR_PTR22]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB30]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR33]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp ule i64 [[SUB]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP23:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT2]] ], [ [[CMP38]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP24]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add i64 [[TMP25]], -1 +// CHECK-NEXT: store i64 [[DEC]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[TMP26:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP41:%.*]] = icmp ugt i64 [[TMP26]], 0 +// CHECK-NEXT: br i1 [[CMP41]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp sge i64 [[TMP28]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP43]]) +// CHECK-NEXT: [[ADD_PTR44:%.*]] = getelementptr inbounds i8, ptr [[TMP27]], i64 [[TMP28]] +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR44]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ult ptr [[WIDE_PTR_PTR46]], [[WIDE_PTR_UB48]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP32]], label [[CONT52:%.*]], label [[TRAP51:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap51: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont52: +// CHECK-NEXT: [[TMP33:%.*]] = icmp uge ptr [[WIDE_PTR_PTR46]], [[WIDE_PTR_LB50]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP33]], label [[CONT54:%.*]], label [[TRAP53:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap53: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont54: +// CHECK-NEXT: [[TMP34:%.*]] = load i8, ptr [[WIDE_PTR_PTR46]], align 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i8 [[TMP34]] to i32 +// CHECK-NEXT: [[TMP35:%.*]] = load i8, ptr [[VAL]], align 1 +// CHECK-NEXT: [[CONV55:%.*]] = sext i8 [[TMP35]] to i32 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CONV55]], [[CONV]] +// CHECK-NEXT: [[CONV56:%.*]] = trunc i32 [[ADD]] to i8 +// CHECK-NEXT: store i8 [[CONV56]], ptr [[VAL]], align 1 +// CHECK-NEXT: br label [[FOR_INC:%.*]] +// CHECK: for.inc: +// CHECK-NEXT: [[TMP36:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP59:%.*]] = icmp sge i64 [[TMP37]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP59]]) +// CHECK-NEXT: [[ADD_PTR61:%.*]] = getelementptr inbounds i8, ptr [[TMP36]], i64 [[TMP37]] +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR61]], ptr [[TMP39]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP40]], align 8 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 0 +// CHECK-NEXT: [[TMP42:%.*]] = load ptr, ptr [[TMP41]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH62:%.*]] = getelementptr i8, ptr [[TMP42]], i64 1 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH62]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 1 +// CHECK-NEXT: [[TMP45:%.*]] = load ptr, ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP45]], ptr [[TMP46]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 2 +// CHECK-NEXT: [[TMP48:%.*]] = load ptr, ptr [[TMP47]], align 8 +// CHECK-NEXT: [[TMP49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP48]], ptr [[TMP49]], align 8 +// CHECK-NEXT: [[TMP50:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[SUB63:%.*]] = sub i64 [[TMP50]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[CMP74:%.*]] = icmp ule ptr [[WIDE_PTR_PTR66]], [[WIDE_PTR_UB73]] +// CHECK-NEXT: br i1 [[CMP74]], label [[LAND_LHS_TRUE76:%.*]], label [[LAND_END105:%.*]] +// CHECK: land.lhs.true76: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB79:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR78]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: [[CMP87:%.*]] = icmp ule ptr [[WIDE_PTR_LB79]], [[WIDE_PTR_PTR82]] +// CHECK-NEXT: br i1 [[CMP87]], label [[LAND_RHS89:%.*]], label [[LAND_END105]] +// CHECK: land.rhs89: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP90]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR91:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP90]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB92:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR91]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP93]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR94:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR95:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR94]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR96:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB97:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR98:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB99:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR98]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST100:%.*]] = ptrtoint ptr [[WIDE_PTR_UB92]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST101:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR95]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB102:%.*]] = sub i64 [[SUB_PTR_LHS_CAST100]], [[SUB_PTR_RHS_CAST101]] +// CHECK-NEXT: [[CMP103:%.*]] = icmp ule i64 [[SUB63]], [[SUB_PTR_SUB102]] +// CHECK-NEXT: br label [[LAND_END105]] +// CHECK: land.end105: +// CHECK-NEXT: [[TMP51:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE76]] ], [ false, [[FOR_INC]] ], [ [[CMP103]], [[LAND_RHS89]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP51]], label [[CONT107:%.*]], label [[TRAP106:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap106: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont107: +// CHECK-NEXT: [[TMP52:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR108:%.*]] = getelementptr inbounds i8, ptr [[TMP52]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR108]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP53:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[DEC109:%.*]] = add i64 [[TMP53]], -1 +// CHECK-NEXT: store i64 [[DEC109]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP5:![0-9]+]] +// CHECK: for.end: +// CHECK-NEXT: [[TMP54:%.*]] = load i8, ptr [[VAL]], align 1 +// CHECK-NEXT: ret i8 [[TMP54]] +// +char for_cond_inc(char *__sized_by(len) p, unsigned long long len) { + char val = *p; + for (p++, len--; len > 0; p++, len--) { + val += *p; + } + return val; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c new file mode 100644 index 0000000000000..6eba217e81de9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c @@ -0,0 +1,93 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int *__counted_by(l) bp; + int l; +}; + +struct FooUnsigned { + int *__counted_by(l) bp; + unsigned long long l; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[11]; + struct Foo f = {arr, 11}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestEdgeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestEdgeOK(void) { + int arr[11]; + struct Foo f = {arr, 1}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestUnsignedEdgeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestUnsignedEdgeOK(void) { + int arr[11]; + struct FooUnsigned f = {arr, 1}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail1(void) { + int arr[11]; + struct Foo f = {arr, 0}; + f.bp = f.bp; + f.l--; +} + + +// CHECK-LABEL: @TestUnsignedCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestUnsignedCountFail(void) { + int arr[11]; + struct FooUnsigned f = {arr, 0}; + f.bp = f.bp; + f.l--; +} + +struct Bar { + int *__counted_by(l - m) bp; + int l; + int m; +}; + +// CHECK-LABEL: @TestBarFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestBarFail(void) { + int arr[11]; + struct Bar f = {arr, 11, 1}; + f.bp = f.bp; + f.l = f.l; + f.m--; // FIXME: rdar://80811527 +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c new file mode 100644 index 0000000000000..f32153eee6861 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c @@ -0,0 +1,187 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + int *__counted_by(l) bp; + int l; +}; + +// XXX: We have two additional null checks for 'tp' due to counted_by to bidi_indexable conversion. +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[TP:%.*]], ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BP]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L1:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP6]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[L1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP7]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END32:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END32]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS29:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs29: +// CHECK-NEXT: [[CMP30:%.*]] = icmp sle i32 0, [[SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP30]], [[LAND_RHS29]] ] +// CHECK-NEXT: br label [[LAND_END32]], {{!annotation ![0-9]+}} +// CHECK: land.end32: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP8]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[BP40:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BP40]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[L1]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP11]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[L1]], align 8 +// CHECK-NEXT: ret void +// +void foo(struct T *tp) { + tp->bp = tp->bp; + tp->l--; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[TP:%.*]], ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP4]], 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ADD]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP6:%.*]] = icmp sle i32 0, [[ADD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP6]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP6]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR10]], ptr [[BP]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP7]], 1 +// CHECK-NEXT: store i32 [[INC]], ptr [[L]], align 8 +// CHECK-NEXT: ret void +// +void bar(struct T *tp) { + int arr[10] = {0}; + tp->bp = arr; + tp->l++; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c new file mode 100644 index 0000000000000..aebd0047f4333 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +// CHECK-LABEL: @f_inout_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void f_inout_count(int *__counted_by(*out_len) buf, int *out_len) {} + +// CHECK-LABEL: @success( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void success() { + int arr[10]; + int len = 10; + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void fail() { + int arr[10]; + int len = 11; + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @pass_out_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[OUT_LEN:%.*]], align 4, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: ret void +// +void pass_out_len(int *__counted_by(*out_len) arr, int *out_len) { + f_inout_count(arr, out_len); +} + +// CHECK-LABEL: @pass_addr_of_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT77:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont77: +// CHECK-NEXT: ret void +// +void pass_addr_of_len(int *__counted_by(len) arr, int len) { + f_inout_count(arr, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c new file mode 100644 index 0000000000000..52d9fd1bf926e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c @@ -0,0 +1,306 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @f_inout_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_LEN:%.*]], ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void f_inout_count(int *__counted_by(*out_len) buf, int *out_len) {} + +// CHECK-LABEL: @pass_addr_of_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[LEN_ADDR]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LEN_ADDR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LEN_ADDR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont4: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB14]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END61:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB24]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP38:%.*]] = icmp ule ptr [[WIDE_PTR_PTR26]], [[WIDE_PTR_PTR33]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP38]], label [[LAND_RHS:%.*]], label [[LAND_END61]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR51]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP56:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP56]], label [[LAND_RHS58:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs58: +// CHECK-NEXT: [[CMP59:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP59]], [[LAND_RHS58]] ] +// CHECK-NEXT: br label [[LAND_END61]], {{!annotation ![0-9]+}} +// CHECK: land.end61: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT4]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT63:%.*]], label [[TRAP62:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap62: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont63: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR73]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT81:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR73]], [[WIDE_PTR_UB75]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT79:%.*]], label [[TRAP78:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap78: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont79: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR73]], [[WIDE_PTR_LB77]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT81]], label [[TRAP80:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap80: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont81: +// CHECK-NEXT: call void @f_inout_count(ptr noundef [[WIDE_PTR_PTR66]], ptr noundef [[WIDE_PTR_PTR73]]) +// CHECK-NEXT: ret void +// +void pass_addr_of_len(int *__counted_by(len) arr, int len) { + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @pass_inout_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_LEN:%.*]], ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END51:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS:%.*]], label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR41]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP46:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP46]], label [[LAND_RHS48:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs48: +// CHECK-NEXT: [[CMP49:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP49]], [[LAND_RHS48]] ] +// CHECK-NEXT: br label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.end51: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @f_inout_count(ptr noundef [[WIDE_PTR_PTR54]], ptr noundef [[TMP6]]) +// CHECK-NEXT: ret void +// +void pass_inout_len(int *__counted_by(*out_len) arr, int *out_len) { + f_inout_count(arr, out_len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c new file mode 100644 index 0000000000000..9fb743db7def7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__counted_by(*len) buf, int *len) { + *len = 42; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__counted_by(*len) buf, int *len) { + buf = buf; + *len = 42; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__counted_by(*len) buf, int *len) { + *len = 42; +bb: + *len = 10; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__counted_by(*len) buf, int *len) { + *len = 42; +bb: + *len = 100; +} + +inline void baz1(int *__counted_by(*len) buf, int *len) { *len = 11; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + int len = 10; + int *__counted_by(len) buf = arr; + baz1(buf, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c new file mode 100644 index 0000000000000..acf3a1e12750e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c @@ -0,0 +1,91 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP6]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: store i32 42, ptr [[TMP8]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(*len) buf, int *len) { + *len = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c new file mode 100644 index 0000000000000..1ab1f110374a4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41 +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41 +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__counted_by(*len) buf, unsigned *len) { + buf = buf; + *len = 42; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41 +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +bb: + *len = 10; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41 +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +bb: + *len = 100; +} + +inline void baz1(int *__counted_by(*len) buf, unsigned *len) { *len = 11; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void baz2(void) { + int arr[10]; + unsigned len = 10; + int *__counted_by(len) buf = arr; + baz1(buf, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c new file mode 100644 index 0000000000000..254ec863928cc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c @@ -0,0 +1,86 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP26]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: store i32 42, ptr [[TMP7]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c new file mode 100644 index 0000000000000..15837bb5b2406 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__sized_by(*size) buf, int *size) { + *size = 40; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__sized_by(*size) buf, int *size) { + buf = buf; + *size = 40; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 8, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__sized_by(*size) buf, int *size) { + *size = 40; +bb: + *size = 8; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__sized_by(*size) buf, int *size) { + *size = 40; +bb: + *size = 100; +} + +inline void baz1(int *__sized_by(*size) buf, int *size) { *size = 64; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + int size = 40; + int *__sized_by(size) buf = arr; + baz1(buf, &size); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c new file mode 100644 index 0000000000000..708d26ea79c73 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c @@ -0,0 +1,104 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.end35: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP9]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: store i32 40, ptr [[TMP11]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__sized_by(*size) buf, int *size) { + *size = 40; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c new file mode 100644 index 0000000000000..527ec14630553 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39 +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39 +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__sized_by(*size) buf, unsigned *size) { + buf = buf; + *size = 40; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39 +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 8, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +bb: + *size = 8; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39 +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +bb: + *size = 100; +} + +inline void baz1(int *__sized_by(*size) buf, unsigned *size) { *size = 64; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void baz2(void) { + int arr[10]; + unsigned size = 40; + int *__sized_by(size) buf = arr; + baz1(buf, &size); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c new file mode 100644 index 0000000000000..2625509caa81a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c @@ -0,0 +1,99 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: store i32 40, ptr [[TMP10]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c new file mode 100644 index 0000000000000..5a9a865690e24 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c @@ -0,0 +1,95 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr, arr + 10 }; +} + +// CHECK-LABEL: @TestRangeFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail1(void) { + int arr[10]; + struct RangePtrs rptrs = { arr + 2, arr + 1, arr + 10 }; +} + +// CHECK-LABEL: @TestRangeFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail2(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr + 2, arr + 1 }; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestStartFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr - 1, arr, arr + 10 }; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP34_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT86:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont86: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestIterFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr + 11, arr + 11 }; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP43_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP28_NOT]], [[CMP43_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT55:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont55: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestEndFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr, arr + 11 }; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c new file mode 100644 index 0000000000000..998d6bf32267a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null 2> /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null 2> /dev/null +// This is to ensure no crash in code gen. A general version of filecheck is in dynamic-range-init-list.c. + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +void Test1(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, 0, arr + 10 }; +} + +void Test2(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, arr, 0 }; +} + +void Test3(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { 0, arr, arr + 1 }; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c new file mode 100644 index 0000000000000..4c32871b0bd73 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c @@ -0,0 +1,233 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[RPTRS:%.*]] = alloca [[STRUCT_RANGEPTRS:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP76:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP87:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP94:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP101:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[ARRAYDECAY6:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY6]], i64 10 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr i32, ptr [[TMP19]], i64 11 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH8]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR11]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_PTR28]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_LB36]], [[WIDE_PTR_PTR39]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP44]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP55:%.*]] = icmp ule ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_UB54]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP55]], label [[LAND_LHS_TRUE56:%.*]], label [[LAND_END84:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true56: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP71:%.*]] = icmp ule ptr [[WIDE_PTR_PTR59]], [[WIDE_PTR_PTR66]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP71]], label [[LAND_RHS72:%.*]], label [[LAND_END84]], {{!annotation ![0-9]+}} +// CHECK: land.rhs72: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP73]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP76]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR78:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB80:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR79]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB82:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR81]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP83:%.*]] = icmp ule ptr [[WIDE_PTR_LB75]], [[WIDE_PTR_PTR78]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END84]], {{!annotation ![0-9]+}} +// CHECK: land.end84: +// CHECK-NEXT: [[TMP31:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE56]] ], [ false, [[CONT]] ], [ [[CMP83]], [[LAND_RHS72]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP31]], label [[CONT86:%.*]], label [[TRAP85:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap85: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont86: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP87]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR88:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR89:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR88]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR90:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB91:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR92:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB93:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR92]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR89]], ptr [[START]], align 8 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP94]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR96:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR97:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB98:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR97]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR99:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB100:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR99]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR96]], ptr [[ITER]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP101]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR103:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR104:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB105:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR104]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR106:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB107:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR106]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR103]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void Test(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, arr + 11, arr + 11 }; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c b/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c new file mode 100644 index 0000000000000..6829d6d60da3e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c @@ -0,0 +1,142 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -emit-llvm %s -o - | FileCheck %s + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: define dso_local i32 @fn_deref( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int fn_deref(int *__ended_by(end) p, int *end) { + return *p; +} + +// CHECK-LABEL: define dso_local i32 @fn_subscript( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP2]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP3]] +// +int fn_subscript(int *__ended_by(end) p, int *end) { + return p[*end - 1]; +} + +// CHECK-LABEL: define dso_local ptr @fn_assign( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 -42 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 42 +// CHECK-NEXT: store ptr [[ADD_PTR1]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP2]] +// +int *fn_assign(int *__ended_by(end) p, int *end) { + end = end - 42; + p = p + 42; + return p; +} + +struct bar { + int *__ended_by(end) q; + int *end; +}; + +// CHECK-LABEL: define dso_local i32 @struct_deref( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int struct_deref(struct bar *b) { + return *b->q; +} + +// CHECK-LABEL: define dso_local i32 @struct_subscript( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP4]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int struct_subscript(struct bar *b) { + return b->q[*b->end - 1]; +} + +// CHECK-LABEL: define dso_local void @struct_assign( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 -42 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END1:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[END1]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 42 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q3:%.*]] = getelementptr inbounds [[STRUCT_BAR]], ptr [[TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ADD_PTR2]], ptr [[Q3]], align 8 +// CHECK-NEXT: ret void +// +void struct_assign(struct bar *b) { + b->end = b->end - 42; + b->q = b->q + 42; +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c new file mode 100644 index 0000000000000..6d816915dad4b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c @@ -0,0 +1,360 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 -1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 1 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR17]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP22]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_LB25]], [[WIDE_PTR_PTR28]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP33]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP30]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP31:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP31]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP31]], align 4 +// CHECK-NEXT: [[TMP32:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR34:%.*]] = getelementptr inbounds i32, ptr [[TMP32]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR34]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP32]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 -1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP0]], align 4 +// WITHOUT-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR1:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i32 1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR1]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP1]], align 4 +// WITHOUT-NEXT: ret void +// +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 -1 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR17]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP22]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_LB25]], [[WIDE_PTR_PTR28]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP33]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP30]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR36]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR43]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// WITHOUT-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// WITHOUT-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// WITHOUT-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// WITHOUT-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// WITHOUT-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// WITHOUT-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// WITHOUT-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// WITHOUT-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR]], align 4 +// WITHOUT-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// WITHOUT-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// WITHOUT-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// WITHOUT-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 -1 +// WITHOUT-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// WITHOUT-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// WITHOUT-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// WITHOUT-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// WITHOUT-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR5]], align 4 +// WITHOUT-NEXT: ret void +// +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c new file mode 100644 index 0000000000000..f5d6fe1e6f838 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c @@ -0,0 +1,81 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -O2 -fbounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -O2 -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef writeonly [[START:%.*]], ptr noundef writeonly [[END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[END]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH3]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33_NOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH3]], [[START]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND41:%.*]] = or i1 [[CMP33_NOT]], [[OR_COND]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND41]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[END]], align 4, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: store i32 0, ptr [[START]], align 4, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr nocapture noundef writeonly [[START:%.*]], ptr nocapture noundef writeonly [[END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: store i32 0, ptr [[END]], align 4, !tbaa [[TBAA2:![0-9]+]] +// WITHOUT-NEXT: store i32 0, ptr [[START]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef writeonly [[START:%.*]], ptr noundef writeonly [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH3]], [[END]], !annotation [[META2]] +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[BOUND_PTR_ARITH3]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33_NOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[START]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND52:%.*]] = or i1 [[CMP33_NOT]], [[OR_COND]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND52]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA3]] +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH3]], align 4, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr nocapture noundef writeonly [[START:%.*]], ptr nocapture noundef writeonly [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH3]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"int", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +//. +// WITHOUT: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// WITHOUT: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// WITHOUT: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// WITHOUT: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c new file mode 100644 index 0000000000000..6546f5b0bbac1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c @@ -0,0 +1,84 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *end; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestOK() { + int arr[10]; + struct S s; + s.start = arr; + s.end = arr + 10; + return 0; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[CMP24_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP35_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP24_NOT]], [[CMP35_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret i32 0 +// +int TestStartFail() { + int arr[10]; + struct S s; + s.start = arr - 1; + s.end = arr + 10; + return 0; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret i32 0 +// +int TestEndFail() { + int arr[10]; + struct S s; + s.start = arr; + s.end = arr + 11; + return 0; +} + +// CHECK-LABEL: @TestRangeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int TestRangeFail() { + int arr[10]; + struct S s; + s.start = arr + 1; + s.end = arr; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c new file mode 100644 index 0000000000000..f6e21df04af11 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c @@ -0,0 +1,148 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *end; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP16]], i64 11 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB9]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_LB27]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP35]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[START43:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[START43]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[END51:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[END51]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo() { + int arr[10]; + struct S s; + s.start = arr - 1; + s.end = arr + 11; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c new file mode 100644 index 0000000000000..170bfa37a0d23 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c @@ -0,0 +1,37 @@ +// XFAIL: * +// BoundsSafety doesn't allow ended_by to reference a __bidi_indexable or __indexable pointer. +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *__bidi_indexable end; +}; + +int Foo(void) { + int arr[10]; + struct S s = {arr, arr + 10}; + int *ptr = s.start; + return 0; +} + +// CHECK: [[STRUCT_TY:%.*]] = type { i32*, [[BIDI_TY:%.*]] } +// CHECK: [[BIDI_TY]] = type { i32*, i32*, i32* } + +// CHECK-LABEL: @Foo +// CHECK: getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC:%.*]], i32 0, i32 0 +// ... +// CHECK: [[S_START_ADDR:%.*]] = getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC:%.*]], i32 0, i32 0 +// CHECK: [[S_START:%.*]] = load i32*, i32** [[S_START_ADDR]], align 8 +// CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR:%.*]], i32 0, i32 0 +// CHECK: store i32* [[S_START]], i32** [[PTR_PTR]], align 8 +// CHECK: [[S_END:%.*]] = getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC]], i32 0, i32 1 +// ... +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64{{.*}} +// CHECK: [[TMP_END_ADDR:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[TEMP_END_LOC:%.*]], i32 0, i32 0 +// CHECK: [[TMP_END_PTR:%.*]] = load i32*, i32** [[TMP_END_ADDR]], align 8 +// CHECK: [[PTR_UPPER:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR]], i32 0, i32 1 +// CHECK: store i32* [[TMP_END_PTR]], i32** [[PTR_UPPER]], align 8 +// CHECK: [[PTR_LOWER:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR]], i32 0, i32 2 +// CHECK: store i32* [[S_START]], i32** [[PTR_LOWER]], align 8 diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c new file mode 100644 index 0000000000000..acf8b2aaceaac --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c @@ -0,0 +1,99 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +static inline void TestOKImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr; + *out_end = arr + 10; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int *end; + int *__ended_by(end) start; + TestOKImpl(&start, &end); +} + +static inline void TestStartFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr - 1; + *out_end = arr + 10; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_I:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[UPPER_I:%.*]] = getelementptr inbounds i8, ptr [[ARR_I]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR_I]], i64 -4 +// CHECK-NEXT: [[CMP24_NOT_I:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH_I]], [[UPPER_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP35_NOT_I:%.*]] = icmp ugt ptr [[ARR_I]], [[BOUND_PTR_ARITH_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = or i1 [[CMP24_NOT_I]], [[CMP35_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[TRAP_I:%.*]], label [[TESTSTARTFAILIMPL_EXIT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: TestStartFailImpl.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestStartFail() { + int *end; + int *__ended_by(end) start; + TestStartFailImpl(&start, &end); +} + +static inline void TestEndFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr; + *out_end = arr + 11; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_I:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER_I:%.*]] = getelementptr inbounds i8, ptr [[ARR_I]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR_I]], i64 44 +// CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH_I]], [[UPPER_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP22_NOT_I:%.*]] = icmp ugt ptr [[ARR_I]], [[BOUND_PTR_ARITH_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = or i1 [[CMP_NOT_I]], [[CMP22_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[TRAP_I:%.*]], label [[TESTENDFAILIMPL_EXIT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: TestEndFailImpl.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestEndFail() { + int *end; + int *__ended_by(end) start; + TestEndFailImpl(&start, &end); +} + +static inline void TestRangeFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr + 1; + *out_end = arr; +} + +// CHECK-LABEL: @TestRangeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail() { + int *end; + int *__ended_by(end) start; + TestRangeFailImpl(&start, &end); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c new file mode 100644 index 0000000000000..4356783cefbbd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[OUT_START:%.*]], ptr [[OUT_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_END:%.*]], ptr [[OUT_END_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP16]], i64 11 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB9]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_LB27]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP35]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[OUT_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP25]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = load ptr, ptr [[OUT_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP26]], align 8 +// CHECK-NEXT: ret void +// +void Foo(int *__ended_by(*out_end) *out_start, int **out_end) { + int arr[10]; + *out_start = arr - 1; + *out_end = arr + 11; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c new file mode 100644 index 0000000000000..2d0e1ba400b9c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c @@ -0,0 +1,180 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +void foo(void); + +// CHECK-LABEL: @TestRangeOK1( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK1(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK2(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr; +} + +// CHECK-LABEL: @TestRangeOK3( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK3(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 10; + s.end = arr + 10; +} + +// CHECK-LABEL: @TestRangeOK4( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK4(void) { + int arr[10]; + struct S s; + s.start = arr + 10; + s.iter = arr + 10; + s.end = arr + 10; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: tail call void @foo() #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH60:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP80_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH60]], [[BOUND_PTR_ARITH8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP95_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH60]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP80_NOT]], [[CMP95_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT96:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont96: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestIterFail(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 2; + s.end = arr + 10; + foo(); + s.start = s.start; + s.iter = s.iter - 3; // is prevented since s.iter - 3 < s.start + s.end = s.end; +} + +// XXX: Why this can be optimized when the next case can't? +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestStartFail(void) { + int arr[10]; + struct S s; + s.start = arr - 1; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP25_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP25_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT48:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont48: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestEndFail(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr + 11; +} + +// CHECK-LABEL: @TestRangeFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail1(void) { + int arr[10]; + struct S s; + s.start = arr + 1; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail2(void) { + int arr[10]; + struct S s; + s.start = arr + 1; + s.iter = arr + 2; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail3(void) { + int arr[10]; + struct S s; + s.start = arr - 1; + s.iter = s.iter; + s.end = s.end; +} + +// CHECK-LABEL: @TestRangeFail4( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail4(void) { + int arr[10]; + struct S s; + s.start = s.start; + s.iter = s.iter; + s.end = s.end + 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c new file mode 100644 index 0000000000000..17dec5a883d49 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c @@ -0,0 +1,291 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +void foo(void); + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP55:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP63:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP107:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP115:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[ITER]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 2 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[ARRAYDECAY6:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY6]], i64 10 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr i32, ptr [[TMP19]], i64 10 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH8]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP23:%.*]] = icmp ule ptr [[WIDE_PTR_LB15]], [[WIDE_PTR_PTR18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP23]], label [[CONT25:%.*]], label [[TRAP24:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap24: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont25: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[START33:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[START33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: [[ITER41:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[ITER41]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: [[END49:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[END49]], align 8 +// CHECK-NEXT: call void @foo() +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER52:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[ITER52]], align 8 +// CHECK-NEXT: [[START53:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[START53]], align 8 +// CHECK-NEXT: [[START54:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP29:%.*]] = load ptr, ptr [[START54]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP29]], ptr [[TMP32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[START58:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[START58]], align 8 +// CHECK-NEXT: [[END59:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[TMP34:%.*]] = load ptr, ptr [[END59]], align 8 +// CHECK-NEXT: [[ITER60:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[ITER60]], align 8 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP35]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP34]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP33]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 0 +// CHECK-NEXT: [[TMP40:%.*]] = load ptr, ptr [[TMP39]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH61:%.*]] = getelementptr i32, ptr [[TMP40]], i64 -3 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH61]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 1 +// CHECK-NEXT: [[TMP43:%.*]] = load ptr, ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP43]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 2 +// CHECK-NEXT: [[TMP46:%.*]] = load ptr, ptr [[TMP45]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP46]], ptr [[TMP47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP63]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER64:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP63]], i32 0, i32 0 +// CHECK-NEXT: [[TMP48:%.*]] = load ptr, ptr [[ITER64]], align 8 +// CHECK-NEXT: [[END65:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP49:%.*]] = load ptr, ptr [[END65]], align 8 +// CHECK-NEXT: [[END66:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP50:%.*]] = load ptr, ptr [[END66]], align 8 +// CHECK-NEXT: [[TMP51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP49]], ptr [[TMP51]], align 8 +// CHECK-NEXT: [[TMP52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP50]], ptr [[TMP52]], align 8 +// CHECK-NEXT: [[TMP53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP48]], ptr [[TMP53]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP67]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR68:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR69:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR68]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB71:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR70]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB73:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR72]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[AGG_TEMP62]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP81:%.*]] = icmp ule ptr [[WIDE_PTR_PTR69]], [[WIDE_PTR_PTR76]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP81]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP50]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR84:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB88:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP96:%.*]] = icmp ule ptr [[WIDE_PTR_PTR84]], [[WIDE_PTR_PTR91]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP54:%.*]] = phi i1 [ false, [[CONT25]] ], [ [[CMP96]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP54]], label [[CONT98:%.*]], label [[TRAP97:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap97: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont98: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP99]], ptr align 8 [[AGG_TEMP50]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR101:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB103:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR104:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB105:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR104]], align 8 +// CHECK-NEXT: [[START106:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR101]], ptr [[START106]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP107]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR108:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR109:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR108]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR110:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB111:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR110]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR112:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB113:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR112]], align 8 +// CHECK-NEXT: [[ITER114:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR109]], ptr [[ITER114]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP115]], ptr align 8 [[AGG_TEMP62]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR116:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR117:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR116]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR118:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB119:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR118]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR120:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB121:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR120]], align 8 +// CHECK-NEXT: [[END122:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR117]], ptr [[END122]], align 8 +// CHECK-NEXT: ret i32 0 +// +int bar(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 2; + s.end = arr + 10; + foo(); + s.start = s.start; + s.iter = s.iter - 3; // is prevented since s.iter - 3 < s.start + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c new file mode 100644 index 0000000000000..7feb309c6ced5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +struct T { + int cnt; + int *__counted_by(cnt) ptr; +}; + +// CHECK-LABEL: @TestIterOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestIterOK() { + int arr[10]; + struct S s = { arr, arr + 10, arr }; + struct T t = { 2, arr + 1 }; + s.start = s.start; + s.iter = t.ptr; // this passes as 's.start <= t.ptr <= s.end' + s.end = s.end; + return 0; +} + +// CHECK-LABEL: @TestIterOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestIterOK2() { + int arr[10]; + struct S s = { arr, arr + 6, arr }; + struct T t = { 4, arr + 6 }; + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +int TestIterFail() { + int arr[10]; + struct S s = { arr, arr + 5, arr }; + struct T t = { 4, arr + 6 }; + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c new file mode 100644 index 0000000000000..9d7120971ab0e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c @@ -0,0 +1,150 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +struct T { + int cnt; + int *__counted_by(cnt) ptr; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca [[STRUCT_T]], align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { i32, ptr }, ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[T_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { i32, ptr }, ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[T_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[S:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ITER]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[START2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[START2]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[T]], i64 16, i1 false) +// CHECK-NEXT: [[CNT:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[CNT]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP8]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP9]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER7:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ITER7]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[END8:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END8]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR12]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8 +// CHECK-NEXT: [[CMP31:%.*]] = icmp ule ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_PTR26]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP31]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[START39:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[START39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: [[ITER47:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[ITER47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[END55:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[END55]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo(struct S s, struct T t) { + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c new file mode 100644 index 0000000000000..e1b25610cedd3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c @@ -0,0 +1,65 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef void (*ended_by_t)(const char *__ended_by(end) start, const char *end); + +void ended_by(const char *__ended_by(end) start, const char *end); + +ended_by_t ended_by_p = &ended_by; + +struct { + char _dummy; + const char array[10]; + char _dummy2; +} x; + +// CHECK-LABEL: @ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @ended_by_p, align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: tail call void [[TMP0]](ptr noundef nonnull getelementptr inbounds (i8, ptr @x, i64 1), ptr noundef nonnull getelementptr inbounds (i8, ptr @x, i64 11)) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: ret void +// +void ok(void) { + ended_by_p(x.array, x.array + 10); +} + +// FIXME: rdar://105524069 +// CHECK-LABEL: @start_fail_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @ended_by_p, align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: tail call void [[TMP0]](ptr noundef nonnull @x, ptr noundef nonnull getelementptr inbounds (i8, ptr @x, i64 11)) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void start_fail_lower(void) { + ended_by_p(x.array - 1, x.array + 10); +} + +// CHECK-LABEL: @start_fail_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void start_fail_upper(void) { + ended_by_p(x.array + 11, x.array + 10); +} + +// CHECK-LABEL: @end_fail_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void end_fail_lower(void) { + ended_by_p(x.array, x.array - 1); +} + +// CHECK-LABEL: @end_fail_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void end_fail_upper(void) { + ended_by_p(x.array, x.array + 11); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-ae48eba2.ll.tmp b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-ae48eba2.ll.tmp new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c new file mode 100644 index 0000000000000..97d28d0768b49 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c @@ -0,0 +1,93 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef void (*ended_by_t)(const char *__ended_by(end) start, const char *end); + +void ended_by(const char *__ended_by(end) start, const char *end); + +ended_by_t ended_by_p = &ended_by; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARRAY:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[ARRAY]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[ARRAY]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr @ended_by_p, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: call void [[TMP15]](ptr noundef [[WIDE_PTR_PTR14]], ptr noundef [[WIDE_PTR_PTR21]]) +// CHECK-NEXT: ret void +// +void foo(void) { + const char array[10]; + ended_by_p(array, array + 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c new file mode 100644 index 0000000000000..6fc47564b3b01 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c @@ -0,0 +1,51 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct EndedByData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +// CHECK-LABEL: @TestIncDecOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncDecOK() { + int arr[10]; + struct EndedByData s = { arr, arr + 2, arr + 10 }; + + s.start++; + s.iter--; // start == iter + s.end--; +} + +// CHECK-LABEL: @TestIncDecTrap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation +// CHECK-NEXT: unreachable +// +void TestIncDecTrap() { + int arr[10]; + struct EndedByData s = { arr + 1, arr + 1, arr + 10 }; + + s.start = s.start; + --s.iter; // trap: start > iter + s.end = s.end; +} + +// CHECK-LABEL: @TestIncDecTrap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation +// CHECK-NEXT: unreachable +// +void TestIncDecTrap2() { + int arr[10]; + struct EndedByData s = { arr, arr + 9, arr + 10 }; + + s.start++; + s.iter++; + s.end--; // trap : iter > end +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c b/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c new file mode 100644 index 0000000000000..c7ec6da986445 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c @@ -0,0 +1,223 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct T { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[T:%.*]], ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ITER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP8]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[ITER1:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 1 +// CHECK-NEXT: [[START4:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[START4]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[ITER1]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[TMP23]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP24]], i64 -1 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 2 +// CHECK-NEXT: [[TMP30:%.*]] = load ptr, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[END6:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP32]], i32 0, i32 2 +// CHECK-NEXT: [[ITER9:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[TMP32]], i32 0, i32 1 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[ITER9]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP34]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP35]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP33]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP40:%.*]] = load ptr, ptr [[TMP39]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH10:%.*]] = getelementptr i32, ptr [[TMP40]], i64 -1 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH10]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 1 +// CHECK-NEXT: [[TMP43:%.*]] = load ptr, ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP43]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 2 +// CHECK-NEXT: [[TMP46:%.*]] = load ptr, ptr [[TMP45]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP46]], ptr [[TMP47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP7]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[AGG_TEMP7]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP29:%.*]] = icmp ule ptr [[WIDE_PTR_PTR17]], [[WIDE_PTR_PTR24]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP29]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP40:%.*]] = icmp ule ptr [[WIDE_PTR_LB32]], [[WIDE_PTR_PTR35]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP48:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP40]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP48]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP51:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_UB50]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_LHS_TRUE52:%.*]], label [[LAND_END80:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true52: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP67:%.*]] = icmp ule ptr [[WIDE_PTR_PTR55]], [[WIDE_PTR_PTR62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP67]], label [[LAND_RHS68:%.*]], label [[LAND_END80]], {{!annotation ![0-9]+}} +// CHECK: land.rhs68: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB71:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR70]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP72]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR74:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR73]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB78:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP79:%.*]] = icmp ule ptr [[WIDE_PTR_LB71]], [[WIDE_PTR_PTR74]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END80]], {{!annotation ![0-9]+}} +// CHECK: land.end80: +// CHECK-NEXT: [[TMP49:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE52]] ], [ false, [[CONT]] ], [ [[CMP79]], [[LAND_RHS68]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP49]], label [[CONT82:%.*]], label [[TRAP81:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap81: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont82: +// CHECK-NEXT: [[TMP50:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP50]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[TMP51:%.*]] = load ptr, ptr [[ITER1]], align 8 +// CHECK-NEXT: [[INCDEC_PTR83:%.*]] = getelementptr inbounds i32, ptr [[TMP51]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR83]], ptr [[ITER1]], align 8 +// CHECK-NEXT: [[TMP52:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[INCDEC_PTR84:%.*]] = getelementptr inbounds i32, ptr [[TMP52]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR84]], ptr [[END6]], align 8 +// CHECK-NEXT: ret void +// +void Test(struct T *t) { + t->start++; + --t->iter; + t->end--; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_locals.c b/clang/test/BoundsSafety/CodeGen/ended_by_locals.c new file mode 100644 index 0000000000000..d4b73f370e7d3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_locals.c @@ -0,0 +1,315 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name NAMED_ --version 3 + +// Regression test for crash tracked by rdar://103382748 + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[ASDF:%.*]], i32 noundef [[ASDF_LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ASDF_LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[MYENDPTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[NAMED_TMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[MYENDEDBYPTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[ASDF_LEN]], ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP1]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP2]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation !2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation !2 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], !annotation !2 +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], !annotation !2 +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, !annotation !2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation !2 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], !annotation !2 +// CHECK-NEXT: br label [[LAND_END]], !annotation !2 +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP16]], [[LAND_RHS]] ], !annotation !2 +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation !2 +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !2 +// CHECK-NEXT: unreachable, !annotation !2 +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP17]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[IDXPROM18:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP13]], i64 [[IDXPROM18]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 2 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[MYENDEDBYPTR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation !2 +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_PTR28]], [[WIDE_PTR_UB35]], !annotation !2 +// CHECK-NEXT: br i1 [[CMP36]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END57:%.*]], !annotation !2 +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation !2 +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_PTR39]], [[WIDE_PTR_PTR28]], !annotation !2 +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END57]], !annotation !2 +// CHECK: land.rhs45: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, !annotation !2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2, !annotation !2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation !2 +// CHECK-NEXT: [[CMP56:%.*]] = icmp ule ptr [[WIDE_PTR_LB48]], [[WIDE_PTR_PTR51]], !annotation !2 +// CHECK-NEXT: br label [[LAND_END57]], !annotation !2 +// CHECK: land.end57: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP56]], [[LAND_RHS45]] ], !annotation !2 +// CHECK-NEXT: br i1 [[TMP27]], label [[CONT59:%.*]], label [[TRAP58:%.*]], !annotation !2 +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !2 +// CHECK-NEXT: unreachable, !annotation !2 +// CHECK: cont59: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[MYENDEDBYPTR]], align 8 +// CHECK-NEXT: ret void +// +// OBJC-LABEL: define dso_local void @foo( +// OBJC-SAME: ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[ASDF:%.*]], i32 noundef [[ASDF_LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// OBJC-NEXT: entry: +// OBJC-NEXT: [[ASDF_LEN_ADDR:%.*]] = alloca i32, align 4 +// OBJC-NEXT: [[MYENDPTR:%.*]] = alloca ptr, align 8 +// OBJC-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[NAMED_TMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[MYENDEDBYPTR:%.*]] = alloca ptr, align 8 +// OBJC-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: store i32 [[ASDF_LEN]], ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: [[TMP0:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP1]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 0 +// OBJC-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// OBJC-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OBJC-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP2]], i64 [[IDXPROM]] +// OBJC-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// OBJC-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 1 +// OBJC-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// OBJC-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// OBJC-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 2 +// OBJC-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// OBJC-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation !8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation !8 +// OBJC-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], !annotation !8 +// OBJC-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], !annotation !8 +// OBJC: land.rhs: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, !annotation !8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation !8 +// OBJC-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], !annotation !8 +// OBJC-NEXT: br label [[LAND_END]], !annotation !8 +// OBJC: land.end: +// OBJC-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP16]], [[LAND_RHS]] ], !annotation !8 +// OBJC-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation !8 +// OBJC: trap: +// OBJC-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !8 +// OBJC-NEXT: unreachable, !annotation !8 +// OBJC: cont: +// OBJC-NEXT: [[TMP11:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP17]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 0 +// OBJC-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// OBJC-NEXT: [[IDXPROM18:%.*]] = sext i32 [[TMP11]] to i64 +// OBJC-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP13]], i64 [[IDXPROM18]] +// OBJC-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP14]], align 8 +// OBJC-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 1 +// OBJC-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// OBJC-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// OBJC-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 2 +// OBJC-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// OBJC-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// OBJC-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP21:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP22:%.*]] = load ptr, ptr [[MYENDEDBYPTR]], align 8 +// OBJC-NEXT: [[TMP23:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// OBJC-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// OBJC-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation !8 +// OBJC-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_PTR28]], [[WIDE_PTR_UB35]], !annotation !8 +// OBJC-NEXT: br i1 [[CMP36]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END57:%.*]], !annotation !8 +// OBJC: land.lhs.true: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation !8 +// OBJC-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_PTR39]], [[WIDE_PTR_PTR28]], !annotation !8 +// OBJC-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END57]], !annotation !8 +// OBJC: land.rhs45: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, !annotation !8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2, !annotation !8 +// OBJC-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation !8 +// OBJC-NEXT: [[CMP56:%.*]] = icmp ule ptr [[WIDE_PTR_LB48]], [[WIDE_PTR_PTR51]], !annotation !8 +// OBJC-NEXT: br label [[LAND_END57]], !annotation !8 +// OBJC: land.end57: +// OBJC-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP56]], [[LAND_RHS45]] ], !annotation !8 +// OBJC-NEXT: br i1 [[TMP27]], label [[CONT59:%.*]], label [[TRAP58:%.*]], !annotation !8 +// OBJC: trap58: +// OBJC-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !8 +// OBJC-NEXT: unreachable, !annotation !8 +// OBJC: cont59: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// OBJC-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[MYENDEDBYPTR]], align 8 +// OBJC-NEXT: ret void +// +void foo(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c new file mode 100644 index 0000000000000..ebc938d622201 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c @@ -0,0 +1,783 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR14]], ptr noundef [[WIDE_PTR_PTR21]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 -2 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i8, ptr [[TMP16]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR11]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP25]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR33]], ptr noundef [[WIDE_PTR_PTR40]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP28]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR31]], ptr noundef [[WIDE_PTR_PTR38]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[ILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META3]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR24]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR36]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, %[[CONT]] ], [ [[CMP41]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT43:.*]], label %[[TRAP42:.*]], !annotation [[META2]] +// CHECK: [[TRAP42]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[BILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR29]], ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR29]], ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP12]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP21:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP28]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR31]], ptr noundef [[WIDE_PTR_PTR38]]) +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c new file mode 100644 index 0000000000000..b5300c71fd93b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c @@ -0,0 +1,934 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR16]], ptr noundef [[WIDE_PTR_PTR23]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP55:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 -2 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i8, ptr [[TMP16]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR11]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP30]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB33]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp ule ptr [[WIDE_PTR_PTR35]], [[WIDE_PTR_PTR42]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP47]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP55]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR57:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB59:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB61:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR60]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR50]], ptr noundef [[WIDE_PTR_PTR57]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP45:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_PTR40]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP45]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR48]], ptr noundef [[WIDE_PTR_PTR55]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[ILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META3]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META3]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR24]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR36]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP56]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR58:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP56]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR58]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT]] ], [ [[CMP61]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !annotation [[META2]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR66]], ptr noundef [[WIDE_PTR_PTR71]]) +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[BILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB29]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP43]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB29]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP43]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP12]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP45:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_PTR40]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP45]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR48]], ptr noundef [[WIDE_PTR_PTR55]]) +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c new file mode 100644 index 0000000000000..5446410edc488 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 -2 +// CHECK-NEXT: [[CMP30_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP30_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef [[BOUND_PTR_ARITH]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 11 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp ugt ptr [[START]], [[END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[END]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[START]], i64 [[IDX_EXT]] +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[ADD_PTR]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c new file mode 100644 index 0000000000000..63b790c61fa7c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c @@ -0,0 +1,142 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 -2 +// CHECK-NEXT: [[CMP30_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP47_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP30_NOT]], [[CMP47_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef [[BOUND_PTR_ARITH]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 11 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp ugt ptr [[START]], [[END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[END]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[START]], i64 [[IDX_EXT]] +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[ADD_PTR]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_returns.c b/clang/test/BoundsSafety/CodeGen/ended_by_returns.c new file mode 100644 index 0000000000000..10cd4d47aa0d1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_returns.c @@ -0,0 +1,363 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://83900556 +// CHECK-LABEL: @chunk( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BEGIN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BEGIN:%.*]], ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END:%.*]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +char *__ended_by(end) chunk(char *__ended_by(end) begin, char *end) { + return begin + 1; +} + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB10]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB12]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR24]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP43]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @chunk(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP28]], align 8 +// CHECK-NEXT: ret void +// +void foo(void) { + int arr[10]; + int *p = chunk(arr, arr+10); +} + +// CHECK-LABEL: @fooCast( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB10]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB12]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR24]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP43]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @chunk(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP28]], align 8 +// CHECK-NEXT: ret void +// +void fooCast(void) { + int arr[10]; + int *p = (int*)chunk(arr, arr+10); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c new file mode 100644 index 0000000000000..359c289a28621 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c @@ -0,0 +1,51 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct flex { + unsigned char count; + unsigned elems[__counted_by(count - 1)]; +}; + +// CHECK-LABEL: @var_decl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr null, ptr [[F]], align 8 +// CHECK-NEXT: ret void +// +void var_decl(void) { + struct flex *__single f = 0; +} + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr null, ptr [[F]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[F]], align 8 +// CHECK-NEXT: ret void +// +void assign(void) { + struct flex *__single f; + f = 0; +} + +// CHECK-LABEL: @return_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +struct flex *return_flex(void) { + return 0; +} + +void take_flex(struct flex *f); + +// CHECK-LABEL: @give_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @take_flex(ptr noundef null) +// CHECK-NEXT: ret void +// +void give_flex(void) { + take_flex(0); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..163a507d627fd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c @@ -0,0 +1,66 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: @init_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void init_single(void *p) { + struct flexible *__single s = p; +} + +// CHECK-LABEL: @init_casted_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} + +// CHECK-LABEL: @assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr null, ptr [[S]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} + +// CHECK-LABEL: @assign_casted_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr null, ptr [[S]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c new file mode 100644 index 0000000000000..8a33c0b3d483e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c @@ -0,0 +1,120 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[FLEX_BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: store ptr [[FLEX_BIDI:%.*]], ptr [[FLEX_BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[FLEX_BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT21:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT15:%.*]], label [[TRAP14:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap14: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT17:%.*]], label [[TRAP16:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap16: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 123, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT21]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont21: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR23]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT31:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_UB25]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT29:%.*]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont29: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_LB27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT31]], label [[TRAP30:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap30: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont31: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR23]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP10]], i32 0, i32 0 +// CHECK-NEXT: store i32 123, ptr [[COUNT]], align 4 +// CHECK-NEXT: ret void +// +void test(flex_t *flex, flex_t *__bidi_indexable flex_bidi) { + flex = flex_bidi; + flex->count = 123; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c new file mode 100644 index 0000000000000..93d6e79ada930 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c @@ -0,0 +1,1435 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +// CHECK-LABEL: define dso_local void @simple_no_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_self_assign( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META3]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !annotation [[META3]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P3]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !annotation [[META3]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P3]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META3]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !annotation [[META3]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META4]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !annotation [[META3]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !annotation [[META3]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !annotation [[META3]] +// CHECK: [[TRAP3]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT4]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !annotation [[META2]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[WIDE_PTR_LB11]], [[WIDE_PTR_PTR7]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT15:.*]], label %[[TRAP14:.*]], !annotation [[META3]] +// CHECK: [[TRAP14]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT15]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_UB25]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END49:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_LB28]], [[WIDE_PTR_PTR31]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP36]], label %[[LAND_RHS:.*]], label %[[LAND_END49]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB39]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR42]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP47]], label %[[LAND_RHS48:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS48]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS48]] ] +// CHECK-NEXT: br label %[[LAND_END49]], !annotation [[META4]] +// CHECK: [[LAND_END49]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT15]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT51:.*]], label %[[TRAP50:.*]], !annotation [[META4]] +// CHECK: [[TRAP50]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT51]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP59]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR61:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB63:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR64:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB65:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR64]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR61]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB63]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT67:.*]], label %[[TRAP66:.*]], !annotation [[META2]] +// CHECK: [[TRAP66]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT67]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB65]], [[WIDE_PTR_PTR61]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT69:.*]], label %[[TRAP68:.*]], !annotation [[META3]] +// CHECK: [[TRAP68]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: [[PTR70:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR61]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[PTR70]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB75]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB77]], [[WIDE_PTR_PTR73]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT81:.*]], label %[[TRAP80:.*]], !annotation [[META3]] +// CHECK: [[TRAP80]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT81]]: +// CHECK-NEXT: [[LEN82:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN82]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !annotation [[META3]] +// CHECK: [[TRAP3]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT4]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !annotation [[META2]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[WIDE_PTR_LB11]], [[WIDE_PTR_PTR7]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT15:.*]], label %[[TRAP14:.*]], !annotation [[META3]] +// CHECK: [[TRAP14]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT15]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_UB25]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END49:.*]], !annotation [[META4]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_LB28]], [[WIDE_PTR_PTR31]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP36]], label %[[LAND_RHS:.*]], label %[[LAND_END49]], !annotation [[META4]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META4]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB39]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR42]] to i64, !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META4]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META4]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP47]], label %[[LAND_RHS48:.*]], label %[[LAND_END:.*]], !annotation [[META4]] +// CHECK: [[LAND_RHS48]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META4]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS48]] ] +// CHECK-NEXT: br label %[[LAND_END49]], !annotation [[META4]] +// CHECK: [[LAND_END49]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT15]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT51:.*]], label %[[TRAP50:.*]], !annotation [[META4]] +// CHECK: [[TRAP50]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT51]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR54]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !annotation [[META2]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB58]], [[WIDE_PTR_PTR54]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !annotation [[META3]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[LEN63:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR54]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN63]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB75]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB77]], [[WIDE_PTR_PTR73]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT81:.*]], label %[[TRAP80:.*]], !annotation [[META3]] +// CHECK: [[TRAP80]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT81]]: +// CHECK-NEXT: [[PTR82:%.*]] = getelementptr inbounds [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR66]], ptr [[PTR82]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_once( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_DOUBLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_both( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_DOUBLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT11:.*]], label %[[TRAP10:.*]], !annotation [[META2]] +// CHECK: [[TRAP10]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT11]]: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !annotation [[META3]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[LEN2:%.*]] = getelementptr inbounds [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: store i32 11, ptr [[LEN2]], align 4 +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META4]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c new file mode 100644 index 0000000000000..ff3d14e80579d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c @@ -0,0 +1,456 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +// CHECK-LABEL: define dso_local void @simple_no_flexbase_update( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: ret void +// +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_update( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_4_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_4_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_4_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_4_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_5_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_self_assign( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[AGG_TEMP29_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP29_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP29_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT53]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update_reverse( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP36_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP36_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP36_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP45_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT53]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_7_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_7_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_7_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_5_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_7_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update_reverse( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_7_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_7_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_7_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_5_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_7_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP36_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP36_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP36_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP45_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT53]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_reverse( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP29_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP29_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP29_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT53]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_5_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT12:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META12:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META12]] +// CHECK: [[CONT12]]: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[OR_COND78:%.*]] = icmp sgt i32 [[TMP3]], 10, !annotation [[META13:![0-9]+]] +// CHECK-NEXT: br i1 [[OR_COND78]], label %[[CONT73:.*]], label %[[TRAP]], !annotation [[META13]] +// CHECK: [[CONT73]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr_reverse( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_5_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT12:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META12]] +// CHECK-NEXT: unreachable, !annotation [[META12]] +// CHECK: [[CONT12]]: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[OR_COND78:%.*]] = icmp sgt i32 [[TMP3]], 10, !annotation [[META13]] +// CHECK-NEXT: br i1 [[OR_COND78]], label %[[CONT56:.*]], label %[[TRAP]], !annotation [[META13]] +// CHECK: [[CONT56]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[AGG_TEMP65_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP65_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TEMP65_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[AGG_TEMP65_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP65_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND77:%.*]] = select i1 [[TMP6]], i1 [[TMP7]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND77]], label %[[CONT73:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT73]]: +// CHECK-NEXT: [[PTR74:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP65_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[PTR74]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_once( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_both( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !annotation [[META6]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: [[AGG_TEMP2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP2_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP2_SROA_2_0_COPYLOAD]], !annotation [[META6]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP2_SROA_0_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND11:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND11]], label %[[CONT10:.*]], label %[[TRAP]], !annotation [[META6]] +// CHECK: [[CONT10]]: +// CHECK-NEXT: [[LEN2:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP2_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: store i32 11, ptr [[LEN2]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META8]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META4]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META12]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-generic"} +// CHECK: [[META13]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c new file mode 100644 index 0000000000000..848427b31585c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c @@ -0,0 +1,372 @@ +// XFAIL: * +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + + +// CHECK-LABEL: @test_lvalue_base_count_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[DOTB:%.*]] = load i1, ptr @test_lvalue_base_count_fail.g_flex.0, align 4 +// CHECK-NEXT: br i1 [[DOTB]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: store i1 true, ptr @test_lvalue_base_count_fail.g_flex.0, align 4 +// CHECK-NEXT: ret void +// +void test_lvalue_base_count_fail() { + static flex_t g_flex = {4, {1, 2, 3, 4}}; + g_flex.count = 5; +} + +// CHECK-LABEL: @test_lvalue_base_count_may_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @test_lvalue_base_count_may_fail.g_flex.0, align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP0]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr @test_lvalue_base_count_may_fail.g_flex.0, align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void test_lvalue_base_count_may_fail() { + static flex_t g_flex = {4, {1, 2, 3, 4}}; + g_flex.count--; +} + + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT46:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT46]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = (flex_t *)arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[20] = {4, 0}; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[20] = {4, 0}; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail() { + char arr[20] = {5, 0}; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail2(flex_t *flex) { + char arr[20] = {5, 0}; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; +} + +// CHECK-LABEL: @test_base_count_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_count_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 5; +} + +// CHECK-LABEL: @test_base_fam_access( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_base_fam_access(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +// CHECK-LABEL: @test_unsafe_base_fam_access( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_unsafe_base_fam_access(flex_t *__unsafe_indexable flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_unsafe_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_unsafe_base_fam_access_fail(flex_t *__unsafe_indexable flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +// CHECK-LABEL: @test_null_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: unreachable +// +void test_null_base_fail(flex_t *__single flex) { + void *p = 0; + flex = p; + flex->count = 4; +} + +// CHECK-LABEL: @test_null_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_null_base_ok(flex_t *__single flex) { + flex = 0; +} + +// CHECK-LABEL: @test_null_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_null_base_fam_access_fail(flex_t *__single flex) { + flex = 0; + + flex->elems[4] = 42; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[20] = {4, 0}; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_init_list_fail() { + char buf[20] = {5, 0}; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[BUF:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(20) [[BUF]], ptr noundef nonnull align 1 dereferenceable(20) @__const.test_flex_argument_ok.buf, i64 20, i1 false) +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[20] = {4, 0}; + flex_t *flex = (flex_t *)buf; + sink(flex); +} + +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_argument_fail() { + char buf[20] = {5, 0}; + flex_t *flex = (flex_t *)buf; + sink(flex); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c new file mode 100644 index 0000000000000..2c5f64da0ffae --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c @@ -0,0 +1,279 @@ +// XFAIL: * +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = oob_ptr; + int a = flex->count; +} + +// XXX: Missing assumption? +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT46:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT46]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[20] = {4, 0}; + flex_t *__single flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[20] = {4, 0}; + flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail() { + char arr[20] = {5, 0}; + flex_t *__single flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail2(flex_t *flex) { + char arr[20] = {5, 0}; + flex = arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; +} + +// CHECK-LABEL: @test_base_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_count_fail(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 5; +} + +// CHECK-LABEL: @test_base_fam_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_fam_access(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[20] = {4, 0}; + flex_wrapper_t flex_wrap = {0, buf}; +} + +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_init_list_fail() { + char buf[20] = {5, 0}; + flex_wrapper_t flex_wrap = {0, buf}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: cont41: +// CHECK-NEXT: [[BUF:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(20) [[BUF]], ptr noundef nonnull align 1 dereferenceable(20) @__const.test_flex_argument_ok.buf, i64 20, i1 false) +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[20] = {4, 0}; + sink(buf); +} + +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_argument_fail() { + char buf[20] = {5, 0}; + sink(buf); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c new file mode 100644 index 0000000000000..3aa7dfbd38b7e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c @@ -0,0 +1,296 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[]; +} flex_t; + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[BOUND_PTR_ARITH]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[CONT8:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = (flex_t *)arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_base_only_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_only_ok() { + char arr[20]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[4]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[4]; + flex = (flex_t *)arr; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_fail() { + char arr[3]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + (void)flex->count; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->elems[0] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_base_fam_access_fail2(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->elems[1] = 0; +} + +// FIXME: rdar://80820825 +// CHECK-LABEL: @test_null_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +void test_null_base_fail(flex_t *__single flex) { + flex = 0; + flex->count = 4; +} + +// CHECK-LABEL: @test_null_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_null_base_ok(flex_t *__single flex) { + flex = 0; +} + +// CHECK-LABEL: @test_null_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_null_base_fam_access_fail(flex_t *__single flex) { + flex = 0; + + flex->elems[4] = 42; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[4]; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_fail() { + char buf[3]; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [4 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[4]; + flex_t *flex = (flex_t *)buf; + sink(flex); +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 3, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_flex_argument_fail() { + char buf[3]; + flex_t *flex = (flex_t *)buf; + sink(flex); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c new file mode 100644 index 0000000000000..3381be8018f1f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c @@ -0,0 +1,142 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned data_const_global_count __unsafe_late_const; + +// CHECK-LABEL: @get_const_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @data_const_global_count, align 4 +// CHECK-NEXT: ret i32 [[TMP0]] +// +__attribute__((const)) int get_const_count() { + return data_const_global_count; +} + +struct struct_flex { + void *dummy; + int fam[__counted_by(get_const_count())]; +}; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT28:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[STRUCT_STRUCT_FLEX:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_STRUCT_FLEX]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT18:%.*]], label [[TRAP17:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont18: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT20:%.*]], label [[TRAP19:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_STRUCT_FLEX]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_const_count() #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[CALL]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont22: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont24: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[CALL]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT28]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT38:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_UB32]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT36:%.*]], label [[TRAP35:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap35: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont36: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_LB34]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT38]], label [[TRAP37:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap37: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont38: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test(struct struct_flex *flex, void *__bidi_indexable new_ptr) { + flex = new_ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c new file mode 100644 index 0000000000000..235a23ee61eb9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct flex { + unsigned char count; + unsigned elems[__counted_by(count)]; +}; + +// CHECK: @f0 = global %struct.flex zeroinitializer, align 4 +struct flex f0 = { }; + +// CHECK: @f1 = global %struct.flex zeroinitializer, align 4 +struct flex f1 = { 0 }; + +// CHECK: @f2 = global %struct.flex zeroinitializer, align 4 +struct flex f2 = { .count = 0 }; + +// CHECK: @f3 = global %struct.flex zeroinitializer, align 4 +struct flex f3 = { 0, {} }; + +// CHECK: @f4 = global %struct.flex zeroinitializer, align 4 +struct flex f4 = { .count = 0, {} }; + +// CHECK: @f5 = global %struct.flex zeroinitializer, align 4 +struct flex f5 = { .count = 0, .elems = {} }; + +// CHECK: @f6 = global { i8, [1 x i32] } { i8 1, [1 x i32] [i32 1] }, align 4 +struct flex f6 = { .count = 1, { 1 } }; + +// CHECK: @f7 = global { i8, [1 x i32] } { i8 1, [1 x i32] [i32 1] }, align 4 +struct flex f7 = { 1, { 1 } }; + +// CHECK: @f8 = global { i8, [1 x i32] } { i8 1, [1 x i32] [i32 1] }, align 4 +struct flex f8 = { .count = 1, { 1 } }; + +// CHECK: @f9 = global { i8, [1 x i32] } { i8 1, [1 x i32] [i32 1] }, align 4 +struct flex f9 = { .count = 1, .elems = { 1 } }; + +// CHECK: @f10 = global { i8, [3 x i32] } { i8 3, [3 x i32] [i32 4, i32 1, i32 2] }, align 4 +struct flex f10 = { .count = 3, { 4, 1, 2 } }; + +// CHECK: @f11 = global { i8, [3 x i32] } { i8 3, [3 x i32] [i32 4, i32 1, i32 2] }, align 4 +struct flex f11 = { .count = 3, { 4, 1, [2] = 2 } }; + +// CHECK: @f12 = global { i8, [3 x i32] } { i8 3, [3 x i32] [i32 4, i32 0, i32 3] }, align 4 +struct flex f12 = { .count = 3, { 4, [2] = 3 } }; + +// CHECK: @f13 = global { i8, [3 x i32] } { i8 3, [3 x i32] [i32 0, i32 0, i32 3] }, align 4 +struct flex f13 = { .count = 3, { [2] = 3 } }; diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c new file mode 100644 index 0000000000000..cee0e1f6ac42d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c @@ -0,0 +1,91 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +// CHECK-LABEL: @good_fit( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_fit(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 10; +} + +// CHECK-LABEL: @good_fit2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_fit2(S *s) { + char arr[40] = {0}; + s = (S *)&arr[1]; + s->offs = 2; + s->len = 9; +} + +// CHECK-LABEL: @good_shrink( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_shrink(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 9; +} + +// CHECK-LABEL: @good_zero( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_zero(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 2; +} + +// CHECK-LABEL: @trap_access_to_count_zero( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_access_to_count_zero(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 2; + s->fam[0] = 0; +} + +// CHECK-LABEL: @trap_len_too_big( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_len_too_big(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 11; +} + +// CHECK-LABEL: @trap_negative_count( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_negative_count(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 10; + s->len = 2; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..1890cafabafa5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c @@ -0,0 +1,201 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + + +// CHECK-LABEL: @trap_len_too_big( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [40 x i8], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 40 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT29:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_S:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_S]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT18:%.*]], label [[TRAP17:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont18: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT20:%.*]], label [[TRAP19:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY21:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont23: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[ARRAYDECAY21]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT25:%.*]], label [[TRAP24:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap24: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont25: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT27:%.*]], label [[TRAP26:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap26: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont27: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 9, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT29]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont29: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR31]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT39:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_UB33]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT37:%.*]], label [[TRAP36:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap36: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont37: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_LB35]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT39]], label [[TRAP38:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap38: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont39: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[OFFS:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP16]], i32 0, i32 1 +// CHECK-NEXT: store i32 2, ptr [[OFFS]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP17]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[FAM41:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY42:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM41]], i64 0, i64 0 +// CHECK-NEXT: [[FAM44:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY45:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM44]], i64 0, i64 0 +// CHECK-NEXT: [[LEN46:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[LEN46]], align 4 +// CHECK-NEXT: [[OFFS47:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 1 +// CHECK-NEXT: [[TMP20:%.*]] = load i32, ptr [[OFFS47]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP19]], [[TMP20]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY45]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB49]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY42]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR51]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY42]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR53]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = icmp ult ptr [[TMP28]], [[WIDE_PTR_UB55]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP29]], label [[CONT59:%.*]], label [[TRAP58:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont59: +// CHECK-NEXT: [[TMP30:%.*]] = icmp uge ptr [[TMP28]], [[WIDE_PTR_LB57]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT61:%.*]], label [[TRAP60:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap60: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont61: +// CHECK-NEXT: store i32 0, ptr [[TMP28]], align 4 +// CHECK-NEXT: ret void +// +void trap_len_too_big(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 11; + + s->fam[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c new file mode 100644 index 0000000000000..b01455c3c7573 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c @@ -0,0 +1,72 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @null_noderef( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_noderef() { + flex_t *__single b = (flex_t *)0; +} + +// CHECK-LABEL: @null_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref() { + flex_t *__single a = (flex_t *)0; + (void)*a; +} + +// CHECK-LABEL: @null_deref2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref2() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + (void)b->elems[0]; +} + +// CHECK-LABEL: @null_deref3( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +void null_deref3() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + b->count = 0; +} + +// CHECK-LABEL: @null_deref_member_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref_member_access() { + flex_t *a = 0; + flex_t *__single b = a; + (*b).count = 10; +} + +// XXX: rdar://103082765 +// CHECK-LABEL: @null_deref_array_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref_array_access() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + (*b).elems[0] = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c new file mode 100644 index 0000000000000..bc07fdbe959ff --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c @@ -0,0 +1,146 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @pointer_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = a; +} + +// CHECK-LABEL: @pointer_assign_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good2() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 11; +} + +typedef struct { + int count; + int elems[__counted_by(count+1)]; +} flex1_t; + +// CHECK-LABEL: @pointer_assign_bidi_oob_notrap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_bidi_oob_notrap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *b = (flex1_t *)a; +} + +// CHECK-LABEL: @pointer_assign_bidi_to_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_assign_bidi_to_single_oob_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *b = (flex1_t *)a; + flex1_t *__single c = b; +} + +// CHECK-LABEL: @pointer_assign_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_assign_single_oob_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *__single b = (flex1_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_single_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_single_good() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *__single b = (flex1_t *)a; + b->count = 9; +} + +// CHECK-LABEL: @pointer_assign_single_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_single_good() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_assign_bidi_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_bidi_good() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_single_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_single_good2() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 9; +} + +// CHECK-LABEL: @pointer_count_assign_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_single_oob_trap() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 11; +} + +// CHECK-LABEL: @pointer_count_assign_single_negative_count_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_single_negative_count_trap() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = -1; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c new file mode 100644 index 0000000000000..86835f429ca9d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c @@ -0,0 +1,397 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @pointer_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +void pointer_assign(flex_t *flex) { + flex_t *b = flex; +} + +// CHECK-LABEL: @pointer_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT35:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[TMP11]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS14:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS14]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR18]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[TMP14]], [[WIDE_PTR_UB20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont26: +// CHECK-NEXT: [[COUNT27:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR18]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[COUNT27]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT29:%.*]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont29: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[ARRAYDECAY15]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT31:%.*]], label [[TRAP30:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap30: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont31: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT33:%.*]], label [[TRAP32:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap32: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont33: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY15]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP17]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT35]], label [[TRAP34:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap34: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont35: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR37]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[BOUNDSCHECK_NOTNULL42:%.*]], label [[CONT46:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull42: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR37]], [[WIDE_PTR_UB39]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP21]], label [[CONT44:%.*]], label [[TRAP43:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap43: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont44: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR37]], [[WIDE_PTR_LB41]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT46]], label [[TRAP45:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap45: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont46: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void pointer_assign_single(flex_t *flex) { + flex_t *__single s = flex; +} + +// CHECK-LABEL: @pointer_and_count_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[B]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[COUNT3:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[COUNT3]], align 4 +// CHECK-NEXT: ret void +// +void pointer_and_count_assign(flex_t *flex) { + flex_t *b = flex; + b->count = 10; +} + +// CHECK-LABEL: @pointer_and_count_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT23:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[TMP11]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS14:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS14]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT17:%.*]], label [[TRAP16:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap16: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[ARRAYDECAY15]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont21: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY15]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 10, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT23]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont23: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[WIDE_PTR_PTR25]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL30:%.*]], label [[CONT34:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull30: +// CHECK-NEXT: [[TMP17:%.*]] = icmp ult ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_UB27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT32:%.*]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont32: +// CHECK-NEXT: [[TMP18:%.*]] = icmp uge ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_LB29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT34]], label [[TRAP33:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap33: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont34: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR25]], ptr [[B]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[B]], align 8 +// CHECK-NEXT: [[COUNT35:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP19]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[COUNT35]], align 4 +// CHECK-NEXT: ret void +// +void pointer_and_count_assign_single(flex_t *flex) { + flex_t *__single b = flex; + b->count = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c new file mode 100644 index 0000000000000..74aef93d34ed2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c @@ -0,0 +1,201 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @set( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[FLEX:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds i8, ptr [[FLEX]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[FLEX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP2_SROA_3_0]], [[FLEX]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[FLEX]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP68_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP68_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[FLEX]], i8 0, i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT89:%.*]] = icmp ne ptr [[FLEX]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT90:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND91:%.*]] = and i1 [[DOTNOT89]], [[DOTNOT90]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND91]], label [[TRAP]], label [[CONT88:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont88: +// CHECK-NEXT: ret ptr [[FLEX]] +// +void *set(flex_t *flex, unsigned size) { + return __builtin_memset(flex, 0, size); +} + +// CHECK-LABEL: @cpy( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT188:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT188]], label [[BOUNDSCHECK_CONT12:%.*]], label [[BOUNDSCHECK_NOTNULL5:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull5: +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT9:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR10:%.*]] = getelementptr inbounds i32, ptr [[ELEMS6]], i64 [[IDX_EXT9]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT12]] +// CHECK: boundscheck.cont12: +// CHECK-NEXT: [[AGG_TEMP4_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR10]], [[BOUNDSCHECK_NOTNULL5]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP4_SROA_3_0]], [[SRC]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP4_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP84_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP84_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP102_NOT:%.*]] = icmp ult ptr [[AGG_TEMP2_SROA_3_0]], [[DEST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND191:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP102_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST154:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST155:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB156:%.*]] = sub i64 [[SUB_PTR_LHS_CAST154]], [[SUB_PTR_RHS_CAST155]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP157_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB156]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND192:%.*]] = select i1 [[OR_COND191]], i1 true, i1 [[CMP157_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND192]], label [[TRAP:%.*]], label [[CONT160:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont160: +// CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT189:%.*]] = icmp ne ptr [[DEST]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT190:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND193:%.*]] = and i1 [[DOTNOT189]], [[DOTNOT190]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND193]], label [[TRAP]], label [[CONT186:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont186: +// CHECK-NEXT: ret ptr [[DEST]] +// +void *cpy(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_memcpy(dest, src, size); +} + +// CHECK-LABEL: @pcpy( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT175:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT175]], label [[BOUNDSCHECK_CONT11:%.*]], label [[BOUNDSCHECK_NOTNULL4:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull4: +// CHECK-NEXT: [[ELEMS5:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT8:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR9:%.*]] = getelementptr inbounds i32, ptr [[ELEMS5]], i64 [[IDX_EXT8]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT11]] +// CHECK: boundscheck.cont11: +// CHECK-NEXT: [[AGG_TEMP3_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR9]], [[BOUNDSCHECK_NOTNULL4]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP3_SROA_3_0]], [[SRC]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP3_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP83_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP83_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP101_NOT:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_3_0]], [[DEST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND176:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP101_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST153:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST154:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB155:%.*]] = sub i64 [[SUB_PTR_LHS_CAST153]], [[SUB_PTR_RHS_CAST154]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP156_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB155]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND177:%.*]] = select i1 [[OR_COND176]], i1 true, i1 [[CMP156_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND177]], label [[TRAP:%.*]], label [[CONT159:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont159: +// CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[DEST]], i64 [[CONV]] +// CHECK-NEXT: ret ptr [[TMP2]] +// +void *__unsafe_indexable pcpy(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_mempcpy(dest, src, size); +} + +// CHECK-LABEL: @move( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT188:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT188]], label [[BOUNDSCHECK_CONT12:%.*]], label [[BOUNDSCHECK_NOTNULL5:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull5: +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT9:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR10:%.*]] = getelementptr inbounds i32, ptr [[ELEMS6]], i64 [[IDX_EXT9]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT12]] +// CHECK: boundscheck.cont12: +// CHECK-NEXT: [[AGG_TEMP4_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR10]], [[BOUNDSCHECK_NOTNULL5]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult ptr [[AGG_TEMP4_SROA_3_0]], [[SRC]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP4_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP84_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP84_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP102_NOT:%.*]] = icmp ult ptr [[AGG_TEMP2_SROA_3_0]], [[DEST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND191:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP102_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST154:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST155:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB156:%.*]] = sub i64 [[SUB_PTR_LHS_CAST154]], [[SUB_PTR_RHS_CAST155]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP157_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB156]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND192:%.*]] = select i1 [[OR_COND191]], i1 true, i1 [[CMP157_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND192]], label [[TRAP:%.*]], label [[CONT160:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont160: +// CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT189:%.*]] = icmp ne ptr [[DEST]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT190:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND193:%.*]] = and i1 [[DOTNOT189]], [[DOTNOT190]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND193]], label [[TRAP]], label [[CONT186:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont186: +// CHECK-NEXT: ret ptr [[DEST]] +// +void *move(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_memmove(dest, src, size); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c new file mode 100644 index 0000000000000..2fb5222527f5c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c @@ -0,0 +1,182 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @set( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[TMP12]] to i64 +// CHECK-NEXT: [[TMP13:%.*]] = bitcast i64 [[CONV]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB21]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br i1 [[CMP35]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB44]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB46]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR56]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB60]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR48]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR62]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP67:%.*]] = icmp ule i64 [[CONV]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP23:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[CMP67]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[WIDE_PTR_PTR71]], i8 0, i64 [[CONV]], i1 false) +// CHECK-NEXT: [[CMP76:%.*]] = icmp sge i64 [[CONV]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP76]]) +// CHECK-NEXT: [[ADD_PTR78:%.*]] = getelementptr inbounds i8, ptr [[WIDE_PTR_PTR71]], i64 [[CONV]] +// CHECK-NEXT: ret void +// +void set(flex_t *flex, unsigned size) { + __builtin_memset(flex, 0, size); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c new file mode 100644 index 0000000000000..01922dd8091fe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c @@ -0,0 +1,458 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +flex_t g_flex = {2, {1, 2}}; + +// CHECK-LABEL: @addrof_g( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @g_flex, align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr getelementptr inbounds ([[STRUCT_FLEX_T:%.*]], ptr @g_flex, i32 0, i32 1), i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT32:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont22: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[WIDE_PTR_LB20]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT28:%.*]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT30:%.*]], label [[TRAP29:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap29: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP12]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT32]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont32: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR34]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT42:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_UB36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_LB38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT42]], label [[TRAP41:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap41: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont42: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR34]] +// +flex_t *addrof_g(void) { + return &g_flex; +} + +// CHECK-LABEL: @addrof_deref_g( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_FLEX_T:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @g_flex, align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr getelementptr inbounds ([[STRUCT_FLEX_T]], ptr @g_flex, i32 0, i32 1), i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TEMP1]], ptr align 4 [[WIDE_PTR_PTR]], i64 4, i1 false) +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY7:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS6]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT8:%.*]] = sext i32 [[TMP6]] to i64 +// CHECK-NEXT: [[ADD_PTR9:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY7]], i64 [[IDX_EXT8]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[AGG_TEMP1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR9]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[AGG_TEMP1]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR15]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ult ptr [[TMP14]], [[WIDE_PTR_UB17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont21: +// CHECK-NEXT: [[TMP16:%.*]] = icmp uge ptr [[TMP14]], [[WIDE_PTR_LB19]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont23: +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP14]], align 4 +// CHECK-NEXT: ret i32 [[TMP17]] +// +int addrof_deref_g(void) { + return (*&g_flex).elems[0]; +} + +// CHECK-LABEL: @assigning_array_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[FLEX:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca [[STRUCT_FLEX_T:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT32:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont22: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB20]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT28:%.*]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT30:%.*]], label [[TRAP29:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap29: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT32]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont32: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR34]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT42:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_UB36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_LB38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT42]], label [[TRAP41:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap41: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont42: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[FLEX]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[FLEX]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL46:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull46: +// CHECK-NEXT: [[ELEMS47:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP14]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY48:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS47]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT49:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[COUNT49]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP16]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY48]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT57:%.*]], label [[TRAP56:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap56: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont57: +// CHECK-NEXT: [[TMP24:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT59:%.*]], label [[TRAP58:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont59: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TEMP44]], ptr align 4 [[WIDE_PTR_PTR51]], i64 4, i1 false) +// CHECK-NEXT: [[ELEMS60:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY61:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS60]], i64 0, i64 0 +// CHECK-NEXT: [[ELEMS63:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY64:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS63]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT65:%.*]] = getelementptr inbounds [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[COUNT65]], align 4 +// CHECK-NEXT: [[IDX_EXT66:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR67:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY64]], i64 [[IDX_EXT66]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[AGG_TEMP44]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR67]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[AGG_TEMP44]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR68:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB69:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR68]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB69]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY61]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR71]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY61]], ptr [[TMP32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR73]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = icmp ult ptr [[TMP33]], [[WIDE_PTR_UB75]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP34]], label [[CONT79:%.*]], label [[TRAP78:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap78: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont79: +// CHECK-NEXT: [[TMP35:%.*]] = icmp uge ptr [[TMP33]], [[WIDE_PTR_LB77]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP35]], label [[CONT81:%.*]], label [[TRAP80:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap80: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont81: +// CHECK-NEXT: store i32 10, ptr [[TMP33]], align 4 +// CHECK-NEXT: ret void +// +void assigning_array_single(flex_t *__bidi_indexable p) { + flex_t *__single flex = p; + (*flex).elems[0] = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c new file mode 100644 index 0000000000000..25f36f393fb01 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c @@ -0,0 +1,161 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +typedef struct { + flex_t *f; +} wflex_t; + +// CHECK-LABEL: @null_init( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init(void) { + wflex_t b = {0}; + flex_t *f = b.f; +} + +// CHECK-LABEL: @null_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_assign(void) { + wflex_t b = {0}; + flex_t *f; + f = b.f; +} + +// CHECK-LABEL: @null_init_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_assign(void) { + wflex_t b = {0}; + flex_t *f = 0; + f = b.f; +} + +// CHECK-LABEL: @null_init_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_init_ret(void) { + wflex_t b = {0}; + flex_t *f = b.f; + return f; +} + +// CHECK-LABEL: @null_assign_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_assign_ret(void) { + wflex_t b = {0}; + flex_t *f = 0; + return f = b.f; +} + +// CHECK-LABEL: @null_init_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_single(void) { + wflex_t b = {0}; + flex_t *__single f = b.f; +} + +// CHECK-LABEL: @null_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_assign_single(void) { + wflex_t b = {0}; + flex_t *__single f = 0; + f = b.f; +} + +// CHECK-LABEL: @null_init_single_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_init_single_ret(void) { + wflex_t b = {0}; + flex_t *__single f = b.f; + return f; +} + +// CHECK-LABEL: @null_init_bidi_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_bidi_to_single(void) { + wflex_t b = {0}; + flex_t *f = b.f; + flex_t *__single f2 = f; +} + + +typedef struct { + int dummy; + flex_t inner; +} flex_wrap_t; + +// CHECK-LABEL: @nested_init( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_init(void) { + flex_wrap_t *a = 0; + flex_wrap_t *__single b = a; +} + + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_assign(void) { + flex_wrap_t *a = 0; + flex_wrap_t *__single b = 0; + b = a; +} + +// CHECK-LABEL: @nested_single_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_single_to_single(void) { + flex_wrap_t *__single a = 0; + flex_wrap_t *__single b = 0; + b = a; +} + +static inline flex_wrap_t *accept_flex(flex_wrap_t *p) { + flex_wrap_t *__single a = p; + return a; +} + +// CHECK-LABEL: @pass_zero_to_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void pass_zero_to_flex(void) { + accept_flex(0); +} + + +// CHECK-LABEL: @nested_return( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_wrap_t *nested_return(void) { + flex_wrap_t *a = 0; + return a; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c new file mode 100644 index 0000000000000..2ecb7c93b32b3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c @@ -0,0 +1,119 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +inline flex_t *return_flex(int *__counted_by(11) buf) { + return (flex_t *)buf; +} + +// CHECK-LABEL: @pointer_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); +} + +// CHECK-LABEL: @pointer_assign_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good2() { + int arr[11] = {10}; + flex_t *__single a; + a = (flex_t *)return_flex(arr); +} + +// CHECK-LABEL: @pointer_init_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_init_good() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); +} + +// CHECK-LABEL: @pointer_init_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_init_good2() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)return_flex(arr); +} + +// CHECK-LABEL: @pointer_count_assign_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_trap() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->count = 11; +} + +// CHECK-LABEL: @pointer_count_init_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_init_trap() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); + a->count = 11; +} + +// CHECK-LABEL: @pointer_count_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->count = 9; +} + +// CHECK-LABEL: @pointer_count_init_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_init_good() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); + a->count = 9; +} + +// CHECK-LABEL: @elem_access_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void elem_access_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->elems[9] = 0; +} + +// CHECK-LABEL: @elem_access_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void elem_access_trap() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->elems[10] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c new file mode 100644 index 0000000000000..f36bff365f700 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c @@ -0,0 +1,560 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP88:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL1:%.*]] = call ptr @bar(i32 noundef [[TMP4]]) +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[MUL:%.*]] = mul i64 4, [[CONV]] +// CHECK-NEXT: [[ADD:%.*]] = add i64 16, [[MUL]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[ADD]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 [[ADD]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB8]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP9]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END39:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP21]], label %[[LAND_RHS:.*]], label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV23:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV23]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label %[[LAND_RHS36:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS36]]: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP37]], %[[LAND_RHS36]] ] +// CHECK-NEXT: br label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_END39]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR43]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label %[[FLEX_BASE_NONNULL:.*]], label %[[CONT68:.*]], !annotation [[META3]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR43]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[TMP11]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT49:.*]], label %[[TRAP48:.*]], !annotation [[META4]] +// CHECK: [[TRAP48]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT49]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB54]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT58:.*]], label %[[TRAP57:.*]], !annotation [[META5]] +// CHECK: [[TRAP57]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT58]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB56]], [[WIDE_PTR_PTR52]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !annotation [[META6]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !annotation [[META7]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB45]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !annotation [[META8]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_LB47]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT66:.*]], label %[[TRAP65:.*]], !annotation [[META9]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, !annotation [[META10:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], !annotation [[META10]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label %[[CONT68]], label %[[TRAP67:.*]], !annotation [[META10]] +// CHECK: [[TRAP67]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META10]] +// CHECK-NEXT: unreachable, !annotation [[META10]] +// CHECK: [[CONT68]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = icmp ne ptr [[WIDE_PTR_PTR70]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT78:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_UB72]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT76:.*]], label %[[TRAP75:.*]], !annotation [[META5]] +// CHECK: [[TRAP75]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT76]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_LB74]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT78]], label %[[TRAP77:.*]], !annotation [[META6]] +// CHECK: [[TRAP77]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP21]], i32 0, i32 0 +// CHECK-NEXT: [[LEN79:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP8]], ptr [[LEN79]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR87:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP22]], i32 0, i32 0 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_INNER]], ptr [[HDR87]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[TMP23]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[BOUNDSCHECK_NOTNULL89:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL89]]: +// CHECK-NEXT: [[FAM90:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY91:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM90]], i64 0, i64 0 +// CHECK-NEXT: [[HDR92:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 0 +// CHECK-NEXT: [[LEN93:%.*]] = getelementptr inbounds [[STRUCT_INNER]], ptr [[HDR92]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[LEN93]], align 8 +// CHECK-NEXT: [[IDX_EXT94:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR95:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY91]], i64 [[IDX_EXT94]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR95]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP28]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP31]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR96:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR97:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR98:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB99:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB101:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR100]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[WIDE_PTR_PTR97]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP32]], label %[[BOUNDSCHECK_NOTNULL102:.*]], label %[[CONT106:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL102]]: +// CHECK-NEXT: [[TMP33:%.*]] = icmp ult ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_UB99]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP33]], label %[[CONT104:.*]], label %[[TRAP103:.*]], !annotation [[META5]] +// CHECK: [[TRAP103]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT104]]: +// CHECK-NEXT: [[TMP34:%.*]] = icmp uge ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_LB101]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP34]], label %[[CONT106]], label %[[TRAP105:.*]], !annotation [[META6]] +// CHECK: [[TRAP105]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT106]]: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR97]] +// +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: define dso_local ptr @foo2( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP88:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL1:%.*]] = call ptr @bar(i32 noundef [[TMP4]]) +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[MUL:%.*]] = mul i64 4, [[CONV]] +// CHECK-NEXT: [[ADD:%.*]] = add i64 16, [[MUL]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[ADD]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 [[ADD]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB8]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP9]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END39:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP21]], label %[[LAND_RHS:.*]], label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV23:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV23]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label %[[LAND_RHS36:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS36]]: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP37]], %[[LAND_RHS36]] ] +// CHECK-NEXT: br label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_END39]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR43]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label %[[FLEX_BASE_NONNULL:.*]], label %[[CONT68:.*]], !annotation [[META3]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR43]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[TMP11]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT49:.*]], label %[[TRAP48:.*]], !annotation [[META4]] +// CHECK: [[TRAP48]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT49]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB54]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT58:.*]], label %[[TRAP57:.*]], !annotation [[META5]] +// CHECK: [[TRAP57]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT58]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB56]], [[WIDE_PTR_PTR52]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !annotation [[META6]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !annotation [[META7]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB45]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !annotation [[META8]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_LB47]], !annotation [[META9]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT66:.*]], label %[[TRAP65:.*]], !annotation [[META9]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], !annotation [[META10]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label %[[CONT68]], label %[[TRAP67:.*]], !annotation [[META10]] +// CHECK: [[TRAP67]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META10]] +// CHECK-NEXT: unreachable, !annotation [[META10]] +// CHECK: [[CONT68]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = icmp ne ptr [[WIDE_PTR_PTR70]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT78:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_UB72]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT76:.*]], label %[[TRAP75:.*]], !annotation [[META5]] +// CHECK: [[TRAP75]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT76]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_LB74]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT78]], label %[[TRAP77:.*]], !annotation [[META6]] +// CHECK: [[TRAP77]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[P]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP79]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR81:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP21]], i32 0, i32 0 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR81]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR86:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP22]], i32 0, i32 0 +// CHECK-NEXT: [[LEN87:%.*]] = getelementptr inbounds [[STRUCT_INNER]], ptr [[HDR86]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP8]], ptr [[LEN87]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[TMP23]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[BOUNDSCHECK_NOTNULL89:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL89]]: +// CHECK-NEXT: [[FAM90:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY91:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM90]], i64 0, i64 0 +// CHECK-NEXT: [[HDR92:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 0 +// CHECK-NEXT: [[LEN93:%.*]] = getelementptr inbounds [[STRUCT_INNER]], ptr [[HDR92]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[LEN93]], align 8 +// CHECK-NEXT: [[IDX_EXT94:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR95:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY91]], i64 [[IDX_EXT94]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR95]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP28]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP31]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR96:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR97:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR98:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB99:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB101:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR100]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[WIDE_PTR_PTR97]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP32]], label %[[BOUNDSCHECK_NOTNULL102:.*]], label %[[CONT106:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL102]]: +// CHECK-NEXT: [[TMP33:%.*]] = icmp ult ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_UB99]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP33]], label %[[CONT104:.*]], label %[[TRAP103:.*]], !annotation [[META5]] +// CHECK: [[TRAP103]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT104]]: +// CHECK-NEXT: [[TMP34:%.*]] = icmp uge ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_LB101]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP34]], label %[[CONT106]], label %[[TRAP105:.*]], !annotation [[META6]] +// CHECK: [[TRAP105]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT106]]: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR97]] +// +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-count-negative"} +// CHECK: [[META8]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-le-upper-bound"} +// CHECK: [[META9]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META10]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c new file mode 100644 index 0000000000000..1e078f5892a94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c @@ -0,0 +1,132 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef [[LEN]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[CALL1:%.*]] = tail call ptr @bar(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[MUL:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[LEN]], -5 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 [[MUL]] +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr i8, ptr [[TMP0]], i64 16 +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP9_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[CALL1]], null, !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label %[[CONT69:.*]], label %[[FLEX_BASE_NONNULL:.*]], !annotation [[META6]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[CALL1]], i64 16 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[CALL1]], [[TMP1]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[DOTNOT106:%.*]] = icmp ugt ptr [[CALL1]], [[TMP0]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT106]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP]], label %[[CONT60:.*]], !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[ADD_PTR3]] to i64, !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP1]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[FLEX_AVAIL_COUNT]], 2, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[CALL1]], [[ADD_PTR3]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND108:%.*]] = select i1 [[FLEX_COUNT_CHECK_NOT]], i1 [[TMP2]], i1 false, !annotation [[META6]] +// CHECK-NEXT: br i1 [[OR_COND108]], label %[[CONT69]], label %[[TRAP]], !annotation [[META9]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: [[LEN70:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN70]], align 8, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr [[CALL1]], align 8, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: ret ptr [[CALL1]] +// +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: define dso_local ptr @foo2( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[CALL1:%.*]] = tail call ptr @bar(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[MUL:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[LEN]], -5 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 [[MUL]] +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr i8, ptr [[TMP0]], i64 16 +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP9_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[CALL1]], null, !annotation [[META6]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label %[[CONT69:.*]], label %[[FLEX_BASE_NONNULL:.*]], !annotation [[META6]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[CALL1]], i64 16 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[CALL1]], [[TMP1]], !annotation [[META7]] +// CHECK-NEXT: [[DOTNOT106:%.*]] = icmp ugt ptr [[CALL1]], [[TMP0]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT106]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP]], label %[[CONT60:.*]], !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[ADD_PTR3]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP1]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[FLEX_AVAIL_COUNT]], 2, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[CALL1]], [[ADD_PTR3]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND108:%.*]] = select i1 [[FLEX_COUNT_CHECK_NOT]], i1 [[TMP2]], i1 false, !annotation [[META6]] +// CHECK-NEXT: br i1 [[OR_COND108]], label %[[CONT69]], label %[[TRAP]], !annotation [[META9]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: store ptr [[CALL]], ptr [[CALL1]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: [[LEN78:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN78]], align 8, !tbaa [[TBAA10]] +// CHECK-NEXT: ret ptr [[CALL1]] +// +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} +//. +// CHECK: [[META2]] = !{[[META3:![0-9]+]]} +// CHECK: [[META3]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META4]] = !{!"bounds-safety-generic"} +// CHECK: [[META5]] = !{!"bounds-safety-generic", !"bounds-safety-check-one-past-end-overflow", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-check-count-negative", !"bounds-safety-check-ptr-le-upper-bound", !"bounds-safety-check-flexible-count-gt-bounds"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META7]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META8]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META9]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +// CHECK: [[TBAA10]] = !{[[META11:![0-9]+]], [[META15:![0-9]+]], i64 8} +// CHECK: [[META11]] = !{!"Inner", [[META12:![0-9]+]], i64 0, [[META15]], i64 8} +// CHECK: [[META12]] = !{!"any pointer", [[META13:![0-9]+]], i64 0} +// CHECK: [[META13]] = !{!"omnipotent char", [[META14:![0-9]+]], i64 0} +// CHECK: [[META14]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META15]] = !{!"int", [[META13]], i64 0} +// CHECK: [[TBAA16]] = !{[[META11]], [[META12]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c new file mode 100644 index 0000000000000..390a31fb9f01b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-incompatible-pointer-types -emit-llvm %s -o - | FileCheck %s +// RN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static char a[42]; +static char b[42]; + +struct Inner { + char * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len - sizeof(struct Inner))]; +}; + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_recursive( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @a, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_recursive(void) { + struct Outer * p = a; + p->hdr.ptr = p; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_margin( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 20, ptr getelementptr inbounds (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_margin(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 20; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_no_fam( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 16, ptr getelementptr inbounds (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_no_fam(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 16; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad(void) { + int len = 43; + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = len; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_neg_count( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 15, ptr getelementptr inbounds (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_neg_count(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 15; // fam has __counted_by(hdr.len - sizeof(struct Inner)), where sizeof(struct Inner) is 16 + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_ptr( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_ptr(void) { + struct Outer * p = a; + p->hdr.ptr = b + 1; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_fam( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr getelementptr inbounds (i8, ptr @a, i64 1), align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds (i8, ptr @a, i64 9), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_fam(void) { + struct Outer * p = a + 1; + p->hdr.ptr = b; + p->hdr.len = 42; + return p; +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META4:![0-9]+]], i64 0} +// CHECK: [[META3]] = !{!"Inner", [[META4]], i64 0, [[META7:![0-9]+]], i64 8} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"int", [[META5]], i64 0} +// CHECK: [[TBAA8]] = !{[[META3]], [[META7]], i64 8} +// CHECK: [[META9]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-check-one-past-end-overflow", !"bounds-safety-check-ptr-le-upper-bound", !"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c b/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c new file mode 100644 index 0000000000000..e5dd64e347c9b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c @@ -0,0 +1,14 @@ +// This is a test to see if the compiler doesn't crash. + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null + +#define a(b, ...) __builtin___memmove_chk(b, __VA_ARGS__, b) +#define c(d, h, e) a(d, h, e) +#define f(g) __attribute__((counted_by(g))) +int n; +void *j; +void l(unsigned char *f(*i), unsigned long *i); +void l(unsigned char *f(*i) k, unsigned long *i) { c(k, (char*)j, n); } diff --git a/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c new file mode 100644 index 0000000000000..bc1f5051aa389 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c @@ -0,0 +1,93 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @check_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound((void *)arr) - (char *)arr == 40) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_forged_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(int *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound_void( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_forged_upper_bound_void(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(void *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +void get_sized_by(void *__sized_by(40) buf); + +// CHECK-LABEL: @int_array_to_void_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: call void @get_sized_by(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void int_array_to_void_good(void) { + int arr[10]; + get_sized_by(arr); +} + +// CHECK-LABEL: @int_array_to_void_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void int_array_to_void_trap(void) { + int arr[9]; + int *p = arr; + get_sized_by(p); +} + +void get_counted_by(int *__counted_by(10) buf); + +// CHECK-LABEL: @int_array_to_int_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @get_counted_by(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void int_array_to_int_good(void) { + int arr[10]; + get_counted_by(arr); +} + +// CHECK-LABEL: @int_array_to_int_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void int_array_to_int_trap(void) { + int arr[9]; + int *p = arr; + get_counted_by(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c new file mode 100644 index 0000000000000..4519dbf191a76 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c @@ -0,0 +1,643 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @check_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: ret i32 0 +// +int check_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound((void *)arr) - (char *)arr == 40) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR8]], i64 99 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR10]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB16]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB22]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[ARRAYDECAY31:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER32:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY31]], i64 10 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY31]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER32]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY31]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB36]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB38]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR24]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR40]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB_PTR_SUB]], 99 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 -1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP26:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP26]] +// +int check_forged_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(int *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound_void( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY4:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY4]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR7]], i64 99 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[ARRAYDECAY24:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER25:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY24]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY24]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER25]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY24]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR27]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB29]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR17]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR33]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB_PTR_SUB]], 99 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 -1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP23]] +// +int check_forged_upper_bound_void(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(void *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +void get_sized_by(void *__sized_by(40) buf); + +// CHECK-LABEL: @int_array_to_void_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @get_sized_by(ptr noundef [[WIDE_PTR_PTR4]]) +// CHECK-NEXT: ret void +// +void int_array_to_void_good(void) { + int arr[10]; + get_sized_by(arr); +} + +// CHECK-LABEL: @int_array_to_void_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP68:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END67:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs66: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.end67: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP68]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: call void @get_sized_by(ptr noundef [[WIDE_PTR_PTR70]]) +// CHECK-NEXT: ret void +// +void int_array_to_void_trap(void) { + int arr[9]; + int *p = arr; + get_sized_by(p); +} + +void get_counted_by(int *__counted_by(10) buf); + +// CHECK-LABEL: @int_array_to_int_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @get_counted_by(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void int_array_to_int_good(void) { + int arr[10]; + get_counted_by(arr); +} + +// CHECK-LABEL: @int_array_to_int_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP6]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @get_counted_by(ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +void int_array_to_int_trap(void) { + int arr[9]; + int *p = arr; + get_counted_by(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/global-indexable.c b/clang/test/BoundsSafety/CodeGen/global-indexable.c new file mode 100644 index 0000000000000..9406588fe2633 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-indexable.c @@ -0,0 +1,27 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +struct SArray { + int arr[10]; +}; + +struct MockWidePtr { + struct SArray *ptr; + struct SArray *ub; + struct SArray *lb; +}; + +// CHECK: @g_mock_wide_ptr = {{.+}} global {{.*}} zeroinitializer, align 8 +struct MockWidePtr g_mock_wide_ptr; +// CHECK: @g_wide_ptr_bidi_indexable = {{.+}} global {{.*}} zeroinitializer, align 8 +int *__bidi_indexable g_wide_ptr_bidi_indexable; +// CHECK: @g_wide_ptr_indexable = {{.+}} global {{.*}} zeroinitializer, align 8 +int *__indexable g_wide_ptr_indexable; +// CHECK: @g_single_ptr = {{.+}} global ptr null, align 8 +int *__single g_single_ptr; +// CHECK: @g_unsafe_indexable_ptr = {{.+}} global ptr null, align 8 +int *__unsafe_indexable g_unsafe_indexable_ptr; diff --git a/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c b/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c new file mode 100644 index 0000000000000..ec176c5ff9781 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +int array[100]; +int* __bidi_indexable ptr = &array[50]; + +// CHECK: _ptr: +// CHECK: .quad _array+200 +// CHECK: .quad _array+400 +// CHECK: .quad _array \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/global-init-with-array.c b/clang/test/BoundsSafety/CodeGen/global-init-with-array.c new file mode 100644 index 0000000000000..dafe1a185f2c7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-init-with-array.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +int array[100][100]; +int* __bidi_indexable ptr = array; + +// CHECK: _ptr: +// CHECK: .quad _array +// CHECK: .quad _array+40000 +// CHECK: .quad _array diff --git a/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c b/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c new file mode 100644 index 0000000000000..cff3248b0dc6f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c @@ -0,0 +1,50 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-bounds-safety-init-list-null-non-zero-count -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-bounds-safety-init-list-null-non-zero-count -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +char array[256]; +char *__counted_by(256) p = array; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @p, align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 256 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[TMP5]], align 1 +// CHECK-NEXT: ret i8 [[TMP8]] +// +char foo(int i) { + return p[i]; +} diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c b/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c new file mode 100644 index 0000000000000..45290b9b84670 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c @@ -0,0 +1,49 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +#include + +extern unsigned externArray[__counted_by(10)]; + +void bar(const unsigned *pointer); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr @externArray, i64 10), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo(void){ + bar(externArray); +} + + diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-array.c b/clang/test/BoundsSafety/CodeGen/incomplete-array.c new file mode 100644 index 0000000000000..07eb45c749e28 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-array.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm -Werror -verify=werr %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -Wno-bounds-safety-incomplete-array -verify=wno -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -verify=default -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -Werror -verify=werr %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -Wno-bounds-safety-incomplete-array -verify=wno -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -verify=default -o /dev/null + +// wno-no-diagnostics +extern unsigned externArray[]; + +void bar(const unsigned *pointer); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo(void){ + // default-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + bar(externArray); // werr-error{{accessing elements of an unannotated incomplete array always fails at runtime}} +} + + diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c b/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c new file mode 100644 index 0000000000000..87314e5453c7f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c @@ -0,0 +1,32 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +#define NULL ((void *__single)0) + +// CHECK-LABEL: @test_null_to_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IMPL_BIDI_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[IMPL_BIDI_PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[IMPL_BIDI_PTR]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr null, i64 1), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[IMPL_BIDI_PTR2]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr null, i64 1), ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8 +// CHECK-NEXT: ret void +// +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (void *)(char *)NULL; +} diff --git a/clang/test/BoundsSafety/CodeGen/index-enum-signed.c b/clang/test/BoundsSafety/CodeGen/index-enum-signed.c new file mode 100644 index 0000000000000..5e80d9ebab663 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-enum-signed.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -fshort-enums -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fshort-enums -emit-llvm %s -o - | FileCheck %s + +#include + +enum bar { + bar_val = -1 +}; + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[B:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[TMP0]] to i8 +// CHECK-NEXT: store i8 [[CONV]], ptr [[B]], align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[B]], align 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i8 [[TMP1]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP2]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int baz(int i) { + int *__bidi_indexable ptr; + enum bar b = i; + return ptr[b]; +} + diff --git a/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c b/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c new file mode 100644 index 0000000000000..18abd746de7e4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c @@ -0,0 +1,51 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -fshort-enums -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fshort-enums -emit-llvm %s -o - | FileCheck %s + +#include + +enum bar { + bar_val = 1 +}; + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[B:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[TMP0]] to i8 +// CHECK-NEXT: store i8 [[CONV]], ptr [[B]], align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[B]], align 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[TMP1]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP2]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int baz(int i) { + int *__bidi_indexable ptr; + enum bar b = i; + return ptr[b]; +} diff --git a/clang/test/BoundsSafety/CodeGen/index-signed.c b/clang/test/BoundsSafety/CodeGen/index-signed.c new file mode 100644 index 0000000000000..b64406053c26d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-signed.c @@ -0,0 +1,48 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[SIGNED_IDX:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SIGNED_IDX]], align 2 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 0 +// +int main() { + int *__bidi_indexable ptr; + + short signed_idx; + ptr[signed_idx]; + return 0; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/index-unsigned.c b/clang/test/BoundsSafety/CodeGen/index-unsigned.c new file mode 100644 index 0000000000000..37b1ce73bf0aa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-unsigned.c @@ -0,0 +1,45 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNSIGNED_IDX:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: store i16 5, ptr [[UNSIGNED_IDX]], align 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[UNSIGNED_IDX]], align 2 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 0 +// +int main() { + int *__bidi_indexable ptr; + + unsigned short unsigned_idx = 5; + ptr[unsigned_idx]; +} + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c b/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c new file mode 100644 index 0000000000000..a88e77a4c2ef7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c @@ -0,0 +1,47 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 + +#include + +int*__indexable bar(int *__indexable ptrArg) { + return ptrArg; +} + +// X86_64-LABEL: @bar( +// X86_64: entry: +// X86_64: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// X86_64: [[PTRARG:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// X86_64: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTRARG]], i32 0, i32 0 +// X86_64: store ptr [[PTRARG_COERCE0:%.*]], ptr [[TMP0]], align 8 +// X86_64: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTRARG]], i32 0, i32 1 +// X86_64: store ptr [[PTRARG_COERCE1:%.*]], ptr [[TMP1]], align 8 +// X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[PTRARG]], i64 16, i1 false) +// X86_64: [[TMP2:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// X86_64: ret { ptr, ptr } [[TMP2]] +// +// AARCH64-LABEL: @bar( +// AARCH64: entry: +// AARCH64: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// AARCH64: [[PTRARG:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// AARCH64: store [2 x i64] [[PTRARG_COERCE:%.*]], ptr [[PTRARG]], align 8 +// AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[PTRARG]], i64 16, i1 false) +// AARCH64: [[TMP0:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// AARCH64: ret [2 x i64] [[TMP0]] +// +// I686-LABEL: @bar( +// I686: entry: +// I686: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// I686: store ptr [[AGG_RESULT:%.*]], ptr [[RESULT_PTR]], align 4 +// I686: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[PTRARG:%.*]], i32 8, i1 false) +// I686: ret void +// + + + + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c b/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c new file mode 100644 index 0000000000000..6d7497fd5b530 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c @@ -0,0 +1,50 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *foo(int *__bidi_indexable ptr) { + return &ptr[4]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c new file mode 100644 index 0000000000000..36688cb80d714 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c @@ -0,0 +1,105 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)&p[index]; +} + +// CHECK-LABEL: @good_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = &p[index]; +} + +// CHECK-LABEL: @good_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)p[index]; +} + +// CHECK-LABEL: @good_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)&p[index]; +} + +// CHECK-LABEL: @bad_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = &p[index]; +} + +// CHECK-LABEL: @bad_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)p[index]; +} + +// CHECK-LABEL: @good_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_oob(void) { + int *__indexable p = foo.array; + int index = 10; + (void)&p[index]; +} + +// CHECK-LABEL: @good_oob_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_oob_assign(void) { + int *__indexable p = foo.array; + int index = 10; + p = &p[index]; +} + +// CHECK-LABEL: @bad_oob_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_oob_deref(void) { + int *__indexable p = foo.array; + int index = 10; + (void)p[index]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c new file mode 100644 index 0000000000000..f42b27b6e6ba9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c @@ -0,0 +1,138 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @ind( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @ind( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable ind(int *__indexable p, int index) { + return &p[index]; +} + +// CHECK-O0-LABEL: @bidi( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: @bidi( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-O2-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-O2-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-O2-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-O2-NEXT: ret void +// +int *__bidi_indexable bidi(int *__indexable p, int index) { + return &p[index]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c new file mode 100644 index 0000000000000..df67889b28fb5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c @@ -0,0 +1,46 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH_OLD:%.*]] = ptrtoint ptr [[TMP4]] to i64 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH_NEW:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge i64 [[BOUND_PTR_ARITH_NEW]], [[BOUND_PTR_ARITH_OLD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[TMP10:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP10]] +// +int *__indexable test(int *__indexable p, int index) { + p += index; + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c new file mode 100644 index 0000000000000..c3a1b8176504a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c @@ -0,0 +1,24 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test(int *__indexable p, int index) { + p += index; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c new file mode 100644 index 0000000000000..c46d21b0ae8a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c @@ -0,0 +1,81 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @foo, i64 44), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_add_one(void) { + int *__indexable p = foo.array; + int index = 1; + p += index; + return p; +} + +// CHECK-LABEL: @good_sub_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @foo, i64 44), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_sub_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + p -= index; + return p; +} + +// CHECK-LABEL: @good_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @foo, i64 80), ptr getelementptr inbounds (i32, ptr getelementptr inbounds ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_oob(void) { + int *__indexable p = foo.array; + int index = 10; + p += index; + return p; +} + +// CHECK-LABEL: @bad_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + p -= index; + return p; +} + +// CHECK-LABEL: @bad_add_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_add_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + p += index; + return p; +} + +// CHECK-LABEL: @bad_add_one_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_add_one_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + p += index; + p -= index; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c new file mode 100644 index 0000000000000..8afd68798c4dc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c @@ -0,0 +1,167 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)(p + index); +} + +// CHECK-LABEL: @good_add_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = p + index; +} + +// CHECK-LABEL: @good_add_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)*(p + index); +} + +// CHECK-LABEL: @good_add_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)(p + index); +} + +// CHECK-LABEL: @bad_add_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = p + index; +} + +// CHECK-LABEL: @bad_add_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)*(p + index); +} + +// CHECK-LABEL: @good_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)(p - index); +} + +// CHECK-LABEL: @bad_sub_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_sub_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = p - index; +} + +// CHECK-LABEL: @bad_sub_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_sub_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)*(p - index); +} + +// CHECK-LABEL: @good_sub_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)(p - index); +} + +// CHECK-LABEL: @good_sub_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = p - index; +} + +// CHECK-LABEL: @good_sub_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)*(p - index); +} + +// CHECK-LABEL: @good_add_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_oob(void) { + int *__indexable p = foo.array; + int index = 10; + (void)(p + index); +} + +// CHECK-LABEL: @good_add_oob_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_oob_assign(void) { + int *__indexable p = foo.array; + int index = 10; + p = p + index; +} + +// CHECK-LABEL: @bad_add_oob_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_oob_deref(void) { + int *__indexable p = foo.array; + int index = 10; + (void)*(p + index); +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c new file mode 100644 index 0000000000000..7d3dd28b45a9c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c @@ -0,0 +1,292 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @add( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @add( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable add(int *__indexable p, int index) { + return p + index; +} + +// CHECK-O0-LABEL: @add2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @add2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable add2(int *__indexable p, int index) { + return index + p; +} + +// CHECK-O0-LABEL: @sub( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[IDX_NEG:%.*]] = sub i64 0, [[IDXPROM]] +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDX_NEG]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @sub( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[IDX_NEG:%.*]] = sub nsw i64 0, [[IDXPROM]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDX_NEG]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable sub(int *__indexable p, int index) { + return p - index; +} + +// CHECK-O0-LABEL: @add_bidi( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: @add_bidi( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-O2-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-O2-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-O2-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-O2-NEXT: ret void +// +int *__bidi_indexable add_bidi(int *__indexable p, int index) { + return index + p; +} diff --git a/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c new file mode 100644 index 0000000000000..67576baea60a0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c @@ -0,0 +1,12 @@ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int *__bidi_indexable bidi_ptr = 0; +int *__bidi_indexable bidi_ptr2 = (int *)(void *)0; + +// CHECK: %[[BSS_STRUCT_NAME:.*]] = type { ptr, ptr, ptr } +// CHECK: @bidi_ptr = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer +// CHECK: @bidi_ptr2 = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer diff --git a/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c new file mode 100644 index 0000000000000..da7ebd1756ee2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int array[3] = {42, 43, 44}; + +int* __bidi_indexable bidi_ptr = array; + +// CHECK: %"[[BSS_BIDI_STRUCT:.*]]" = type { ptr, ptr, ptr } + +// CHECK: [[ARRAY:.*]] = +// CHECK: {{.*}} = {{.*}} %"[[BSS_BIDI_STRUCT]]" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr [[ARRAY]] to i64), i64 12) to ptr), ptr [[ARRAY]] }, align 8 diff --git a/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c new file mode 100644 index 0000000000000..24c26471676c5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c @@ -0,0 +1,12 @@ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +int* __indexable ptr = 0; +int* __indexable ptr2 = (int *)(void *)0; + +// CHECK: %[[BSS_STRUCT_NAME:.*]] = type { ptr, ptr } +// CHECK: @ptr = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer +// CHECK: @ptr2 = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer diff --git a/clang/test/BoundsSafety/CodeGen/init-global-indexable.c b/clang/test/BoundsSafety/CodeGen/init-global-indexable.c new file mode 100644 index 0000000000000..5750d26047948 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-indexable.c @@ -0,0 +1,15 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int array[3] = {42, 43, 44}; + +int* __indexable ptr = array; + +// CHECK: %"[[BSS_IDX_STRUCT:.*]]" = type { ptr, ptr } +// CHECK: [[ARRAY:.*]] = +// CHECK: {{.*}} = {{.*}} %"[[BSS_IDX_STRUCT]]" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr [[ARRAY]] to i64), i64 12) to ptr) }, align 8 + diff --git a/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c new file mode 100644 index 0000000000000..9a855a5777461 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c @@ -0,0 +1,13 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int* __single ptr = 0; +int* __single ptr2 = (int *)(void *)0; + +// CHECK: @ptr = {{.*}} ptr null +// CHECK: @ptr2 = {{.*}} ptr null + diff --git a/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c b/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c new file mode 100644 index 0000000000000..f54cce33fe792 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable inner; + int *__indexable inner2; +}; +int globalCh; + +struct Foo global = { + .inner = &globalCh, + .inner2 = &globalCh +}; + +// CHECK: _global: +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh+4 +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh+4 diff --git a/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c b/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c new file mode 100644 index 0000000000000..72cb52b3846fe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c @@ -0,0 +1,17 @@ + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct s { int arr[10]; char *cp; }; + +void *g1 = __unsafe_forge_single(void *, 1); +struct s *g2 = __unsafe_forge_single(struct s*, 2); +void *__bidi_indexable g3 = __unsafe_forge_bidi_indexable(void *, 3, 10); +struct s *__bidi_indexable g4 = __unsafe_forge_bidi_indexable(void *, 4, sizeof(struct s)); + +// CHECK: @g1 = global ptr inttoptr (i64 1 to ptr), align 8 +// CHECK: @g2 = global ptr inttoptr (i64 2 to ptr), align 8 +// CHECK: @g3 = global %"__bounds_safety::wide_ptr.bidi_indexable" { ptr inttoptr (i64 3 to ptr), ptr inttoptr (i64 13 to ptr), ptr inttoptr (i64 3 to ptr) }, align 8 +// CHECK: @g4 = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr inttoptr (i64 4 to ptr), ptr inttoptr (i64 52 to ptr), ptr inttoptr (i64 4 to ptr) }, align 8 diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c new file mode 100644 index 0000000000000..c6922368062de --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &globalCh + }; +} + +// CHECK: local: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh + + diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c new file mode 100644 index 0000000000000..73469f1dd3547 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c @@ -0,0 +1,47 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &localCh + }; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LOCALCH:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[LOCALCH]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: ret void +// diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c new file mode 100644 index 0000000000000..80f5efa96438c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c @@ -0,0 +1,47 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &localCh + }; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LOCALCH:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[LOCALCH]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: ret void +// \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c new file mode 100644 index 0000000000000..a5880e2dfd7e0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c @@ -0,0 +1,2065 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Checks that are the same between -fbounds-safety-bringup-missing-checks flags +// RUN: %clang_cc1 -O0 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s +// RUN: %clang_cc1 -O0 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s + +// Checks that differ with -fbounds-safety-bringup-missing-checks +// RUN: %clang_cc1 -O0 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=LEGACY %s +// RUN: %clang_cc1 -O0 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=NEW %s +#include + + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cb([2 x i64] [[TMP8]]) +// SAME-NEXT: ret void +// +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// SAME-LABEL: define dso_local void @init_list_cb_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cb([2 x i64] [[TMP3]]) +// SAME-NEXT: ret void +// +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cb([2 x i64] [[TMP6]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cb([2 x i64] [[TMP8]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cb([2 x i64] [[TMP1]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cb([2 x i64] [[TMP3]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#endif + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3:![0-9]+]] +// SAME-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// SAME: [[BOUNDSCHECK_NOTNULL]]: +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// SAME-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// SAME: [[BOUNDSCHECK_NULL]]: +// SAME-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr null, ptr [[TMP7]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr null, ptr [[TMP8]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr null, ptr [[TMP9]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT]] +// SAME: [[BOUNDSCHECK_CONT]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cbon([2 x i64] [[TMP13]]) +// SAME-NEXT: ret void +// +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_cbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cbon([2 x i64] [[TMP4]]) +// SAME-NEXT: ret void +// +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META2:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP9]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cbon([2 x i64] [[TMP10]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3:![0-9]+]] +// NEW-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP9]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cbon([2 x i64] [[TMP13]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cbon([2 x i64] [[TMP1]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cbon([2 x i64] [[TMP4]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#endif + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sb([2 x i64] [[TMP8]]) +// SAME-NEXT: ret void +// +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// SAME-LABEL: define dso_local void @init_list_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sb([2 x i64] [[TMP3]]) +// SAME-NEXT: ret void +// +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sb([2 x i64] [[TMP6]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sb([2 x i64] [[TMP8]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sb([2 x i64] [[TMP1]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sb([2 x i64] [[TMP3]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#endif + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// SAME-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// SAME: [[BOUNDSCHECK_NOTNULL]]: +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// SAME-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// SAME: [[BOUNDSCHECK_NULL]]: +// SAME-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr null, ptr [[TMP7]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr null, ptr [[TMP8]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr null, ptr [[TMP9]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT]] +// SAME: [[BOUNDSCHECK_CONT]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sbon([2 x i64] [[TMP13]]) +// SAME-NEXT: ret void +// +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_sbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sbon([2 x i64] [[TMP4]]) +// SAME-NEXT: ret void +// +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP9]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sbon([2 x i64] [[TMP10]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META3]] +// NEW-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP9]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sbon([2 x i64] [[TMP13]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sbon([2 x i64] [[TMP1]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sbon([2 x i64] [[TMP4]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#endif +//. +// SAME: [[META2]] = !{!"bounds-safety-generic"} +// SAME: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +//. +// LEGACY: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +//. +// NEW: [[META2]] = !{!"bounds-safety-generic"} +// NEW: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c new file mode 100644 index 0000000000000..ce751389c862f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c @@ -0,0 +1,685 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Checks that are the same between -fbounds-safety-bringup-missing-checks flags +// RUN: %clang_cc1 -O2 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s +// RUN: %clang_cc1 -O2 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s + +// Checks that differ with -fbounds-safety-bringup-missing-checks +// RUN: %clang_cc1 -O2 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=LEGACY %s +// RUN: %clang_cc1 -O2 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=NEW %s +#include + + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2:![0-9]+]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4:[0-9]+]] +// SAME-NEXT: ret void +// +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// SAME-LABEL: define dso_local void @init_list_cb_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2:[0-9]+]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2:![0-9]+]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4:[0-9]+]] +// NEW-NEXT: ret void +// +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7:![0-9]+]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#endif + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META9:![0-9]+]] +// SAME-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// SAME-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// SAME-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_cbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS_CONT_CRIT_EDGE]]: +// SAME-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: br label %[[CONT:.*]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// SAME-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META9:![0-9]+]] +// NEW-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// NEW-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// NEW-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS_CONT_CRIT_EDGE]]: +// NEW-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: br label %[[CONT:.*]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// NEW-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#endif + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// SAME-LABEL: define dso_local void @init_list_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#endif + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META9]] +// SAME-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// SAME-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// SAME-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_sbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS_CONT_CRIT_EDGE]]: +// SAME-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: br label %[[CONT:.*]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// SAME-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META9]] +// NEW-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// NEW-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// NEW-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr nocapture noundef readonly [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS_CONT_CRIT_EDGE]]: +// NEW-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: br label %[[CONT:.*]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META7]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// NEW-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#endif +//. +// SAME: [[META2]] = !{!"bounds-safety-generic"} +// SAME: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// SAME: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// SAME: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// SAME: [[META6]] = !{!"Simple C/C++ TBAA"} +// SAME: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// SAME: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// SAME: [[META9]] = !{!"bounds-safety-check-ptr-neq-null"} +//. +// NEW: [[META2]] = !{!"bounds-safety-generic"} +// NEW: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// NEW: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// NEW: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// NEW: [[META6]] = !{!"Simple C/C++ TBAA"} +// NEW: [[META7]] = !{!"bounds-safety-generic", [[META8:![0-9]+]]} +// NEW: [[META8]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// NEW: [[META9]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-global.c b/clang/test/BoundsSafety/CodeGen/init-struct-global.c new file mode 100644 index 0000000000000..1f1b635e7cb6d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-global.c @@ -0,0 +1,17 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +struct Foo global = {&globalCh}; + +// CHECK: _global: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-local.c b/clang/test/BoundsSafety/CodeGen/init-struct-local.c new file mode 100644 index 0000000000000..cc63b9c47d7db --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-local.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +void ignoreme(void) { + struct Foo local = { + .inner = &globalCh + }; +} + +// CHECK: local: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh diff --git a/clang/test/BoundsSafety/CodeGen/init-union-global.c b/clang/test/BoundsSafety/CodeGen/init-union-global.c new file mode 100644 index 0000000000000..996a6b0a04824 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-union-global.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +union Foo { + char *__bidi_indexable inner; + int a; + unsigned b[100]; +}; +char globalCh; + +union Foo global = { + .inner = &globalCh +}; + +// CHECK: _global: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+1 +// CHECK: .quad _globalCh \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c b/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c new file mode 100644 index 0000000000000..45e01e3217f17 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c @@ -0,0 +1,28 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @.str, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr @.str, i64 5), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @.str, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void asm sideeffect "test $0", "r,~{dirflag},~{fpsr},~{flags}"(ptr [[WIDE_PTR_PTR]]) #[[ATTR1:[0-9]+]], {{!srcloc ![0-9]+}} +// CHECK-NEXT: ret void +// +void Test() { + __asm__ ("test %0" ::"r" (("beef"))); +} diff --git a/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c b/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c new file mode 100644 index 0000000000000..2281ca85ff083 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +// XXX: Previously, rdar://132269322 was caused by incorrectly emitting 'align 16' here +// CHECK-LABEL: define dso_local float @large_array_subscript( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [80 x float], align 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 320, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [80 x float], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[ARRAYDECAY]], i64 80 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr float, ptr [[ARRAYDECAY]], i64 19 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[UPPER]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARRAYDECAY]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP2:%.*]] = load float, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret float [[TMP2]] +// +float large_array_subscript(void) { + float arr[80] = {0}; + return arr[19]; +} + +// CHECK-LABEL: define dso_local float @vla_subscript( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-NEXT: store ptr [[TMP2]], ptr [[SAVED_STACK]], align 8 +// CHECK-NEXT: [[VLA:%.*]] = alloca float, i64 [[TMP1]], align 16 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[VLA]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[VLA]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[VLA]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr float, ptr [[WIDE_PTR_PTR]], i64 19 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP8:%.*]] = load float, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP9]]) +// CHECK-NEXT: ret float [[TMP8]] +// +float vla_subscript(int size) { + float arr[size]; + return arr[19]; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c b/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c new file mode 100644 index 0000000000000..5177562c7badd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c @@ -0,0 +1,122 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s + +#include + +struct opaq_t; + +struct opaq_t *g; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @g, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END41:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP21]], label [[LAND_RHS:%.*]], label [[LAND_END41]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB32]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP39:%.*]] = icmp sle i64 11, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS40:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs40: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS40]] ] +// CHECK-NEXT: br label [[LAND_END41]], {{!annotation ![0-9]+}} +// CHECK: land.end41: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void test() { + struct opaq_t *__sized_by(11) ptr = __unsafe_forge_bidi_indexable(struct opaq_t *, g, 11); +} diff --git a/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c b/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c new file mode 100644 index 0000000000000..151992f450f4f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c @@ -0,0 +1,30 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct foo *bar(void); + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @bar() #[[ATTR2:[0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[CALL]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[TMP0]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[ADD_PTR]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable baz(void) { + return bar()->p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/member-expr.c b/clang/test/BoundsSafety/CodeGen/member-expr.c new file mode 100644 index 0000000000000..920c88a78532b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/member-expr.c @@ -0,0 +1,998 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +struct foo { + int bar; + int baz; + int frob[10]; +}; + +void escape_ptr_single(int *__single p); +void escape_ptr_bidi(int *__bidi_indexable p); + +// CHECK-LABEL: @foo_single_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_bar_single(struct foo *__single f) { + escape_ptr_single(&f->bar); +} + +// CHECK-LABEL: @foo_single_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_baz_single(struct foo *__single f) { + escape_ptr_single(&f->baz); +} + +// CHECK-LABEL: @foo_single_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_frob_single(struct foo *__single f) { + escape_ptr_single(f->frob); +} + +// CHECK-LABEL: @foo_single_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_frob4_single(struct foo *__single f) { + escape_ptr_single(&f->frob[4]); +} + +// CHECK-LABEL: @foo_single_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_bar_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->bar); +} + +// CHECK-LABEL: @foo_single_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_baz_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->baz); +} + +// CHECK-LABEL: @foo_single_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_frob_bidi(struct foo *__single f) { + escape_ptr_bidi(f->frob); +} + +// CHECK-LABEL: @foo_single_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_frob4_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->frob[4]); +} + +// CHECK-LABEL: @foo_bidi_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_bar_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->bar); +} + +// CHECK-LABEL: @foo_bidi_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_baz_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->baz); +} + +// CHECK-LABEL: @foo_bidi_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob_single(struct foo *__bidi_indexable f) { + escape_ptr_single(f->frob); +} + +// CHECK-LABEL: @foo_bidi_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob4_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->frob[4]); +} + +// CHECK-LABEL: @foo_bidi_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_bar_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->bar); +} + +// CHECK-LABEL: @foo_bidi_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_baz_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->baz); +} + +// CHECK-LABEL: @foo_bidi_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(f->frob); +} + +// CHECK-LABEL: @foo_bidi_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob4_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->frob[4]); +} + +// CHECK-LABEL: @foo_dot_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_bar_single(void) { + struct foo f; + escape_ptr_single(&f.bar); +} + +// CHECK-LABEL: @foo_dot_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_baz_single(void) { + struct foo f; + escape_ptr_single(&f.baz); +} + +// CHECK-LABEL: @foo_dot_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob_single(void) { + struct foo f; + escape_ptr_single(f.frob); +} + +// CHECK-LABEL: @foo_dot_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob4_single(void) { + struct foo f; + escape_ptr_single(&f.frob[4]); +} + +// CHECK-LABEL: @foo_dot_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_bar_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.bar); +} + +// CHECK-LABEL: @foo_dot_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_baz_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.baz); +} + +// CHECK-LABEL: @foo_dot_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob_bidi(void) { + struct foo f; + escape_ptr_bidi(f.frob); +} + +// CHECK-LABEL: @foo_dot_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob4_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.frob[4]); +} diff --git a/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c b/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c new file mode 100644 index 0000000000000..181ed1e742f94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c @@ -0,0 +1,64 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -triple x86_64 -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 -o - | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[16] = { 0 }; + struct T t; + t.cnt1 = 5; + t.cnt2 = 2; + t.ptr = arr; +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[16] = { 0 }; + int n = 7; + struct T t; + t.cnt1 = n; + t.cnt2 = 1; + t.ptr = arr; +} + +// CHECK-LABEL: @TestOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK2() { + int arr[16] = { 0 }; + struct T t; + t.cnt1 = 7; + t.cnt2 = -1; + t.ptr = arr; +} + +// CHECK-LABEL: @TestFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void TestFail2() { + int arr[16] = { 0 }; + int n = 7; + struct T t; + t.cnt1 = n; + t.cnt2 = -5; + t.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/multiple-dependees.c b/clang/test/BoundsSafety/CodeGen/multiple-dependees.c new file mode 100644 index 0000000000000..432c59fefbd26 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/multiple-dependees.c @@ -0,0 +1,165 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -triple x86_64 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm -o - | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [16 x i32], align 16 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca [[STRUCT_T]], align 8 +// CHECK-NEXT: store i32 [[IDX:%.*]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 64, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [16 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 16 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[CNT1:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[CNT1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CNT2:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[CNT2]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[PTR1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END29:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END29]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 16, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS28:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs28: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS28]] ] +// CHECK-NEXT: br label [[LAND_END29]], {{!annotation ![0-9]+}} +// CHECK: land.end29: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[CNT130:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 5, ptr [[CNT130]], align 8 +// CHECK-NEXT: [[CNT231:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store i32 2, ptr [[CNT231]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[PTR39:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[T]], i64 16, i1 false) +// CHECK-NEXT: [[CNT142:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[CNT142]], align 8 +// CHECK-NEXT: [[CNT243:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[CNT243]], align 4 +// CHECK-NEXT: [[PTR44:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR44]], align 8 +// CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 2, [[TMP5]] +// CHECK-NEXT: [[MUL45:%.*]] = mul nsw i32 3, [[TMP6]] +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[MUL]], [[MUL45]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR47]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[TMP12]], [[WIDE_PTR_UB49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT53:%.*]], label [[TRAP52:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap52: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont53: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[TMP12]], [[WIDE_PTR_LB51]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT55:%.*]], label [[TRAP54:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap54: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont55: +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP12]], align 4 +// CHECK-NEXT: ret i32 [[TMP15]] +// +int Foo(int idx) { + int arr[16] = { 0 }; + int *ptr = arr; + struct T t; + t.cnt1 = 5; + t.cnt2 = 2; + t.ptr = ptr; + + return t.ptr[idx]; +} diff --git a/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c b/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c new file mode 100644 index 0000000000000..3ec3aaeb333fe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c @@ -0,0 +1,75 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple armv7k -target-feature +neon -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple armv7k -target-feature +neon -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[SIGN:%.*]] = alloca [64 x float], align 4 +// CHECK-NEXT: [[R:%.*]] = alloca [[STRUCT_FLOAT32X4X4_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 4 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 4 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 4 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLOAT32X4X4_T]], ptr [[R]], i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[R]], ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[R]], ptr [[TMP3]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [64 x float], ptr [[SIGN]], i32 0, i32 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[ARRAYDECAY]], i32 64 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP7]], align 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 4 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP9]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 4 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[TMP10]], align 4 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB13]], ptr [[TMP11]], align 4 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB15]], ptr [[TMP12]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 4 +// CHECK-NEXT: [[VLD4Q_V:%.*]] = call { <4 x float>, <4 x float>, <4 x float>, <4 x float> } @llvm.arm.neon.vld4.v4f32.p0(ptr [[WIDE_PTR_PTR17]], i32 1) +// CHECK-NEXT: store { <4 x float>, <4 x float>, <4 x float>, <4 x float> } [[VLD4Q_V]], ptr [[WIDE_PTR_PTR3]], align 8 +// CHECK-NEXT: ret void +// +void test() { + float sign[64]; + float32x4x4_t r; + __builtin_neon_vld4q_v(&r, sign, 41); +} diff --git a/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c new file mode 100644 index 0000000000000..afaf95aed73c0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c @@ -0,0 +1,107 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +// CHECK-LABEL: define dso_local i8 @access( +// CHECK-SAME: ptr noundef readonly [[BAR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds i8, ptr [[BAR]], i64 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds i8, ptr [[BAR]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[FAM]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[FAM]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[FAM]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP:%.*]], !annotation [[META7]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: cont8: +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: ret i8 [[TMP3]] +// +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + + + +// CHECK-LABEL: define dso_local ptr @assign( +// CHECK-SAME: ptr nocapture noundef readonly [[BAR:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BAR]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_BAR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BAR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_BAR_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_BAR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[BAR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_BAR_SROA_IDX]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], null, !annotation [[META13:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label [[CONT46:%.*]], label [[FLEX_BASE_NONNULL:%.*]], !annotation [[META13]] +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[TMP0]], !annotation [[META14:![0-9]+]] +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !annotation [[META14]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META15:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META15]] +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_3_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT18:%.*]], label [[TRAP]], !annotation [[META7]] +// CHECK: cont18: +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META16:![0-9]+]] +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_2_0_COPYLOAD]] to i64, !annotation [[META17:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META17]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META17]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation [[META17]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT]], [[FLEX_COUNT_INTPTR]], !annotation [[META17]] +// CHECK-NEXT: [[OR_COND49:%.*]] = select i1 [[FLEX_COUNT_MINUS]], i1 [[FLEX_COUNT_CHECK]], i1 false, !annotation [[META17]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: [[OR_COND60:%.*]] = select i1 [[OR_COND49]], i1 [[TMP3]], i1 false, !annotation [[META7]] +// CHECK-NEXT: br i1 [[OR_COND60]], label [[BOUNDSCHECK_CONT:%.*]], label [[TRAP]], !annotation [[META16]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[LEN31:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: br label [[CONT46]], !annotation [[META13]] +// CHECK: cont46: +// CHECK-NEXT: [[LEN31_SINK:%.*]] = phi ptr [ [[LEN31]], [[BOUNDSCHECK_CONT]] ], [ inttoptr (i64 4 to ptr), [[ENTRY:%.*]] ] +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN31_SINK]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META4:![0-9]+]], i64 4} +// CHECK: [[META3]] = !{!"Inner", [[META4]], i64 0, [[META4]], i64 4} +// CHECK: [[META4]] = !{!"int", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META8]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META9]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA10]] = !{[[META5]], [[META5]], i64 0} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"any pointer", [[META5]], i64 0} +// CHECK: [[META13]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META14]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META15]] = !{!"bounds-safety-check-one-past-end-overflow", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-check-count-negative", !"bounds-safety-check-ptr-le-upper-bound", !"bounds-safety-check-flexible-count-gt-bounds"} +// CHECK: [[META16]] = !{!"bounds-safety-check-count-negative"} +// CHECK: [[META17]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c new file mode 100644 index 0000000000000..fd9a167154235 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c @@ -0,0 +1,270 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +// CHECK-LABEL: define dso_local i8 @access( +// CHECK-SAME: ptr noundef [[BAR:%.*]], i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BAR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[BAR]], ptr [[BAR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[INDEX]], ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BAR_ADDR]], align 8 +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_OUTER:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FAM2:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM2]], i64 0, i64 0 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[TMP10]], [[WIDE_PTR_UB7]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[TMP10]], [[WIDE_PTR_LB]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT9:%.*]], label [[TRAP8:%.*]], !annotation [[META3]] +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont9: +// CHECK-NEXT: [[TMP13:%.*]] = load i8, ptr [[TMP10]], align 1 +// CHECK-NEXT: ret i8 [[TMP13]] +// +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + + +// CHECK-LABEL: define dso_local ptr @assign( +// CHECK-SAME: ptr noundef [[BAR:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BAR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[BAR]], ptr [[BAR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BAR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT28:%.*]], !annotation [[META4]] +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP4]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT18:%.*]], label [[TRAP17:%.*]], !annotation [[META2]] +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont18: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT20:%.*]], label [[TRAP19:%.*]], !annotation [[META3]] +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP3]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT22:%.*]], label [[TRAP21:%.*]], !annotation [[META6]] +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont22: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB7]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT24:%.*]], label [[TRAP23:%.*]], !annotation [[META7]] +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: cont24: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT26:%.*]], label [[TRAP25:%.*]], !annotation [[META8]] +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: cont26: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP3]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT]], !annotation [[META9]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT28]], label [[TRAP27:%.*]], !annotation [[META9]] +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: cont28: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT38:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_UB32]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT36:%.*]], label [[TRAP35:%.*]], !annotation [[META2]] +// CHECK: trap35: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont36: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_LB34]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT38]], label [[TRAP37:%.*]], !annotation [[META3]] +// CHECK: trap37: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont38: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[S]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[S]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[LEN39:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[LEN39]], align 4 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[S]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP15]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL41:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull41: +// CHECK-NEXT: [[FAM42:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY43:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM42]], i64 0, i64 0 +// CHECK-NEXT: [[HDR44:%.*]] = getelementptr inbounds [[STRUCT_OUTER]], ptr [[TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[LEN45:%.*]] = getelementptr inbounds [[STRUCT_INNER]], ptr [[HDR44]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[LEN45]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY43]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP23]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[WIDE_PTR_PTR47]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP24]], label [[BOUNDSCHECK_NOTNULL52:%.*]], label [[CONT56:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull52: +// CHECK-NEXT: [[TMP25:%.*]] = icmp ult ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_UB49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT54:%.*]], label [[TRAP53:%.*]], !annotation [[META2]] +// CHECK: trap53: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont54: +// CHECK-NEXT: [[TMP26:%.*]] = icmp uge ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_LB51]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP26]], label [[CONT56]], label [[TRAP55:%.*]], !annotation [[META3]] +// CHECK: trap55: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont56: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR47]] +// +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META6]] = !{!"bounds-safety-check-count-negative"} +// CHECK: [[META7]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-le-upper-bound"} +// CHECK: [[META8]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META9]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/noalias-attribute.c b/clang/test/BoundsSafety/CodeGen/noalias-attribute.c new file mode 100644 index 0000000000000..681745e8c4060 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/noalias-attribute.c @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -O0 -triple x86_64 -std=gnu99 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -std=gnu99 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Check if 'noalias' attribute is added to thin pointers and dropped for wide pointers. + +// __bidi_indexable + +// CHECK-DAG: declare void @bidi_indexable_callee(ptr dead_on_unwind writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8, ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8) +int *__bidi_indexable bidi_indexable_callee(int *restrict __bidi_indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local void @bidi_indexable_caller(ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 %agg.result, ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 %p) +int *__bidi_indexable bidi_indexable_caller(int *restrict __bidi_indexable p) { + // CHECK-DAG: call void @bidi_indexable_callee(ptr dead_on_unwind writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[_:.*]], ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[_:.*]]) + return bidi_indexable_callee(p); +} + +// __indexable + +// CHECK-DAG: declare { ptr, ptr } @indexable_callee(ptr noundef, ptr noundef) +int *__indexable indexable_callee(int *restrict __indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local { ptr, ptr } @indexable_caller(ptr noundef %p.coerce0, ptr noundef %p.coerce1) +int *__indexable indexable_caller(int *restrict __indexable p) { + // CHECK-DAG: call { ptr, ptr } @indexable_callee(ptr noundef [[_:.*]], ptr noundef [[_:.*]]) + return indexable_callee(p); +} + +// __single + +// CHECK-DAG: declare noalias ptr @single_callee(ptr noundef) +int *__single single_callee(int *restrict __single p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @single_caller(ptr noalias noundef %p) +int *__single single_caller(int *restrict __single p) { + // CHECK-DAG: call noalias ptr @single_callee(ptr noundef [[_:.*]]) + return single_callee(p); +} + +// __unsafe_indexable + +// CHECK-DAG: declare noalias ptr @unsafe_indexable_callee(ptr noundef) +int *__unsafe_indexable unsafe_indexable_callee(int *restrict __unsafe_indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @unsafe_indexable_caller(ptr noalias noundef %p) +int *__unsafe_indexable unsafe_indexable_caller(int *restrict __unsafe_indexable p) { + // CHECK-DAG: call noalias ptr @unsafe_indexable_callee(ptr noundef [[_:.*]]) + return unsafe_indexable_callee(p); +} + +// __counted_by + +// CHECK-DAG: declare noalias ptr @counted_by_callee(ptr noundef) +int *__counted_by(8) counted_by_callee(int *restrict __counted_by(8) p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @counted_by_caller(ptr noalias noundef %p) +int *__counted_by(8) counted_by_caller(int *restrict __counted_by(8) p) { + // CHECK-DAG: call noalias ptr @counted_by_callee(ptr noundef [[_:.*]]) + return counted_by_callee(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/null-to-bound.c b/clang/test/BoundsSafety/CodeGen/null-to-bound.c new file mode 100644 index 0000000000000..30fbb8598c57c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/null-to-bound.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int main() { + int *__bidi_indexable ptrBound = 0; + int *__indexable ptrArray = 0; + + return 0; +} + +// CHECK: [[BOUND_STRUCT:%.*]] = type { ptr, ptr, ptr } +// CHECK: [[ARRAY_STRUCT:%.*]] = type { ptr, ptr } +// CHECK: [[PTR_BOUND:%.*]] = alloca [[BOUND_STRUCT]], align 8 +// CHECK: [[PTR_ARRAY:%.*]] = alloca [[ARRAY_STRUCT]], align 8 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[PTR_BOUND]], i8 0, i64 24, i1 false) +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[PTR_ARRAY]], i8 0, i64 16, i1 false) diff --git a/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c b/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c new file mode 100644 index 0000000000000..8c51a1bebcabc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c @@ -0,0 +1,363 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple aarch64 %s -o - | FileCheck %s --check-prefix=NOPQ +// RUN: %clang_cc1 -opaque-pointers -O0 -fbounds-safety -emit-llvm -triple aarch64 %s -o - | FileCheck %s --check-prefixes=OPQ + +#include + +struct s1 { + int dummy; + _Bool flag; +}; + +// NOPQ-LABEL: @f1( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i32 0, i32 1 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1 +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// NOPQ: boundscheck.notnull: +// NOPQ-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont: +// NOPQ-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap1: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont2: +// NOPQ-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +// OPQ-LABEL: @f1( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i32 0, i32 1 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1 +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// OPQ: boundscheck.notnull: +// OPQ-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont: +// OPQ-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap1: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont2: +// OPQ-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +_Bool *f1(struct s1 *p) { + return &p->flag; +} + +// NOPQ-LABEL: @f2( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// NOPQ-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// NOPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// NOPQ-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont: +// NOPQ-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap1: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont2: +// NOPQ-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP1]], align 1 +// NOPQ-NEXT: [[TOBOOL:%.*]] = trunc i8 [[TMP4]] to i1 +// NOPQ-NEXT: ret i1 [[TOBOOL]] +// +// OPQ-LABEL: @f2( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// OPQ-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// OPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// OPQ-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont: +// OPQ-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap1: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont2: +// OPQ-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP1]], align 1 +// OPQ-NEXT: [[TOBOOL:%.*]] = trunc i8 [[TMP4]] to i1 +// OPQ-NEXT: ret i1 [[TOBOOL]] +// +_Bool f2(_Bool *__bidi_indexable p, int i) { + return p[i]; +} + +// NOPQ-LABEL: @f3( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// NOPQ-NEXT: [[CONV:%.*]] = zext i32 [[TMP1]] to i64 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CONV]] +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// NOPQ-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// NOPQ-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f3( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// OPQ-NEXT: [[CONV:%.*]] = zext i32 [[TMP1]] to i64 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CONV]] +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// OPQ-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// OPQ-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// OPQ-NEXT: ret void +// +_Bool *__bidi_indexable f3(_Bool *p, unsigned size) { + return __unsafe_forge_bidi_indexable(_Bool *, p, size); +} + +// NOPQ-LABEL: @f4( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// NOPQ: terminated_by.loop_cond: +// NOPQ-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// NOPQ-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// NOPQ-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// NOPQ-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// NOPQ: terminated_by.loop_cont: +// NOPQ-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// NOPQ-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// NOPQ: terminated_by.loop_end: +// NOPQ-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// NOPQ-NEXT: ret [2 x i64] [[TMP5]] +// +// OPQ-LABEL: @f4( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// OPQ: terminated_by.loop_cond: +// OPQ-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// OPQ-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// OPQ-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// OPQ-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// OPQ: terminated_by.loop_cont: +// OPQ-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// OPQ-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// OPQ: terminated_by.loop_end: +// OPQ-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// OPQ-NEXT: ret [2 x i64] [[TMP5]] +// +_Bool *__indexable f4(_Bool *__null_terminated p) { + return __null_terminated_to_indexable(p); +} + +// NOPQ-LABEL: @f5( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[L:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 1 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f5( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[L:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 1 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: ret void +// +void f5(_Bool *p) { + _Bool *l = p; +} + +// no trap +// NOPQ-LABEL: @f6( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: store [2 x i64] [[P_COERCE:%.*]], ptr [[P]], align 8 +// NOPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f6( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: store [2 x i64] [[P_COERCE:%.*]], ptr [[P]], align 8 +// OPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// OPQ-NEXT: ret void +// +_Bool *__bidi_indexable f6(_Bool *__indexable p) { + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c new file mode 100644 index 0000000000000..3b74ba5968073 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c @@ -0,0 +1,884 @@ + + +// RUN: %clang_cc1 -fbounds-safety -Os %s -triple arm64-apple-iphoneos -emit-llvm -o %t-Os.s -opt-record-file %t-Os.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-Os.opt.yaml --check-prefixes OPT-REM %s + +#include + +static inline int *__bidi_indexable is_counted_by(int * __counted_by(n) ptr, unsigned n) { + return ptr; +} + +void test_shl(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 * N); + for(int i= 0 ; i < N; i++) { + t[2*i] = 1; + } +} + +void test_mul(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 5 * N); + for(int i= 0 ; i < N; i++) { + t[2*i] = 1; + } +} + +void test_add(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 + N); + for(int i= 0 ; i < N; i++) { + t[i] = 1; + } +} + +void test_sub(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N - 2); + for(int i= 0 ; i < N; i++) { + t[i] = 1; + } +} + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 12 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 35 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''shl'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 33 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 26 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 15, Column: 13 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 12 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 35 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''mul'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 33 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 26 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 22, Column: 13 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 12 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 35 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''add'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 33 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 26 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 29, Column: 11 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 12 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 35 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''add'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 33 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 26 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 36, Column: 11 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c new file mode 100644 index 0000000000000..f8e577b9d80b0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c @@ -0,0 +1,901 @@ + + +// RUN: %clang_cc1 -fbounds-safety -Os %s -triple arm64-apple-iphoneos -emit-llvm -o %t-Os.s -opt-record-file %t-Os.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-Os.opt.yaml --check-prefixes OPT-REM %s + +#include +static inline int *__bidi_indexable is_counted_by(int * __counted_by(n) ptr, unsigned n) { + return ptr; +} + +void ptr_induction_different_step_sign(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N); + int *t_start = t; + for(int i = N - 1; i >= 0; i--) { + *t_start = 1; + t_start += 1; + } +} + +void ptr_induction_different_step_sign_2(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N); + int *t_end = t + N - 1; + for(int i = 0; i < N; i++) { + *t_end = 1; + t_end -= 1; + } +} + +void ptr_induction_different_step_size(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 * N); + for(int i = 0 ; i < N; i++) { + *t = 1; + t += 2; + } +} + +void ptr_induction_different_step_size2(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 3 * N); + for(int i = 0 ; i < N; i+=2) { + *t = 1; + t += 3; + } +} + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 15, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 24, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 35 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''shl'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 32, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 35 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''mul'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 40, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c new file mode 100644 index 0000000000000..6d4d7fc961c4e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c @@ -0,0 +1,326 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -Wno-bounds-safety-init-list -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int len = 0; + char *__single __counted_by(len) dcp = *argv; + char *__single tp = dcp; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr noundef %argv) +// IR: store i32 %argc, ptr %[[ARGC_ALLOCA:[a-z0-9.]+]] +// IR: store ptr %argv, ptr %[[ARGV_ALLOCA:[a-z0-9.]+]] +// IR: %[[ARGV:[a-z0-9.]+]] = load ptr, ptr %[[ARGV_ALLOCA]]{{.*}} +// IR: %[[ARGV_DEREF:[a-z0-9.]+]] = load ptr, ptr %[[ARGV]]{{.*}} +// IR: %[[LEN_DEREF:[a-z0-9.]+]] = load i32, ptr %{{.*}}, !dbg ![[LOC_11_20:[0-9]+]] +// IR: %[[LEN_EXT:[a-z0-9.]+]] = sext i32 %[[LEN_DEREF]] to i64, !dbg ![[LOC_11_20]] +// IR: icmp ule ptr %[[ARGV_DEREF]], {{.*}}, !dbg ![[LOC_11_37:[0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:[a-z0-9.]+]], label %[[LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_11_37]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: icmp ule ptr {{.*}}, %[[ARGV_DEREF]], !dbg ![[LOC_11_44:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[LABEL_CONT2:.+]], label %[[LABEL_TRAP_RES]], !dbg ![[LOC_11_44]] + +// IR: [[LABEL_CONT2]]: +// ... +// IR: icmp sle i64 %[[LEN_EXT]], {{.*}}, !dbg ![[LOC_11_44]] +// IR: br i1 %{{.*}}, label %[[LABEL_CONT3:.+]], label %[[LABEL_TRAP_RES2:.+]], !dbg ![[LOC_11_44]] + +// IR: [[LABEL_CONT3]]: +// IR: %[[LEN_CHECK_RES:[a-z0-9_]+]] = icmp sle i64 0, %[[LEN_EXT]], !dbg ![[LOC_11_44]] +// IR: br label %[[LABEL_TRAP_RES2]] + +// IR: [[LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[LABEL_CONT2]] ], [ %[[LEN_CHECK_RES]], %[[LABEL_CONT3]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[LABEL_TRAP_RES]] + +// IR: [[LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[LABEL_CONT]] ], [ false, %entry ], [ %[[TRAP_RES2]], %[[LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label %[[LABEL_CONT4:[a-z0-9.]+]], label %[[LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_11_44]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_44:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_44]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[LABEL_CONT4]]: +// IR: %[[NULL_CHECK_RES:[a-z0-9_]+]] = icmp ne ptr %[[WIDE_PTR:[a-z0-9_.]+]], null, !dbg ![[LOC_TMP2:[0-9]+]], !annotation ![[ANNOT1:[0-9]+]] +// IR: br i1 %[[NULL_CHECK_RES]], label %[[LABEL_CONT5:[a-z0-9.]+]], label %[[LABEL_END:[a-z0-9.]+]], !dbg ![[LOC_TMP:[0-9]+]], !annotation ![[ANNOT2:[0-9]+]] + +// IR: [[LABEL_CONT5]]: +// IR: %[[LT_CHECK_RES:[a-z0-9_]+]] = icmp ult ptr %[[WIDE_PTR]], {{.*}}, !dbg ![[LOC_TMP]], !annotation ![[ANNOT3:[0-9]+]] +// IR: br i1 %[[LT_CHECK_RES]], label %[[LABEL_CONT6:[a-z0-9.]+]], label %[[LABEL_TRAP2:[a-z0-9.]+]], !dbg ![[LOC_TMP]], !annotation ![[ANNOT3]] + +// IR: [[LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC2:[0-9]+]], !annotation ![[ANNOT_TRAP:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC2]], !annotation ![[ANNOT_TRAP]] + +// IR: [[LABEL_CONT6]]: +// IR: %[[GE_CHECK_RES:[a-z0-9_]+]] = icmp uge ptr %[[WIDE_PTR]], {{.*}}, !dbg ![[LOC_TMP]], !annotation ![[ANNOT4:[0-9]+]] +// IR: br i1 %[[GE_CHECK_RES]], label %[[LABEL_CONT7:[a-z0-9.]+]], label %[[LABEL_TRAP3:[a-z0-9.]+]], !dbg ![[LOC_TMP]], !annotation ![[ANNOT4]] + +// IR: [[LABEL_END]]: + +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// IR-DAG: ![[TRAP_LOC_11_44]] = !DILocation(line: 0, scope: ![[TRAP_INFO_11_44:[0-9]+]], inlinedAt: ![[LOC_11_44]]) +// IR-DAG: ![[TRAP_INFO_11_44]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" +// +// IR-DAG: ![[LOC_11_20]] = !DILocation(line: 11, column: 20 + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_TRAP]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '43' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 44 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '38' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 25 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-neq-null, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ne (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c new file mode 100644 index 0000000000000..52153468fd46d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c @@ -0,0 +1,149 @@ +// REQUIRES: system-darwin + +// RUN: %clang_cc1 -triple x86_64-apple-macos -Wno-bounds-safety-init-list -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int len = 0; + char *__single __counted_by(len) dcp = *argv; + char *__single tp = dcp; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr nocapture noundef readonly %argv) +// IR: %[[ARGV_DEREF:[a-z0-9.]+]] = load ptr, ptr %argv, align 8, !dbg ![[LOC_11_44:[0-9]+]] +// IR: %[[NULL_CHECK:[a-z0-9.]+]] = icmp eq ptr %[[ARGV_DEREF]], null, !dbg ![[LOC_12_25:[0-9]+]], !annotation ![[NULL_CHECK_ANNOT:[0-9]+]] +// IR: br i1 %[[NULL_CHECK]], label %[[LABEL_CONT:[a-z0-9.]+]], label %[[LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_12_25]], !annotation ![[NULL_CHECK_ANNOT]] + +// IR: [[LABEL_TRAP_RES]]: +// IR: tail call void @llvm.ubsantrap(i8 25) #2, !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ALL_TRAPS_ANNOT:[0-9]+]] +// IR: unreachable, !dbg ![[TRAP_LOC_MISSING]] + +// IR: [[LABEL_CONT]]: +// IR: ret i32 0, !dbg ![[LOC_13_1:[0-9]+]] + +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// +// IR-DAG: ![[LOC_12_25]] = !DILocation(line: 12, column: 25 + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// TODO: The annotations on the branch and trap instruction should match (rdar://109089053) +// IR-DAG: ![[ALL_TRAPS_ANNOT]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[NULL_CHECK_ANNOT]] = !{!"bounds-safety-check-ptr-neq-null"} + +// opt-rem checks auto-generated by gen-opt-remarks-check-lines.py + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 12, Column: 25 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c new file mode 100644 index 0000000000000..aa3c032407ac8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c @@ -0,0 +1,426 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n, unsigned idx) { + array[idx] = 42; +} + +int main(int argc, char **argv) { + unsigned n = argc; + int * a; + foo(a, n, argc - 1); +} + +// IR-LABEL: @foo +// ... +// IR: icmp ult {{.*}} !dbg ![[LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// IR-NEXT: unreachable, !dbg ![[LT_TRAP_LOC_10_16]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR: icmp uge {{.*}} !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP2:[a-z0-9]+]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB]] +// ... +// IR: [[FOO_LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[GE_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: unreachable, !dbg ![[GE_TRAP_LOC_10_16]], !annotation ![[ANNOT_GE_LB]] + +// IR-LABEL: @main +// IR: entry +// ... +// IR-DAG: call void @llvm.memset{{.*}}, !annotation ![[ANNOT_AUTO_INIT:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %entry ], [ %[[WIDTH_CHECK_RES]], %[[MAIN_LABEL_CONT2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_AUTO_INIT]] = !{!"bounds-safety-zero-init"} + +// IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16{{.*}}) +// IR-DAG: ![[LT_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) +// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[GE_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[GE_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) +// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 13, {{.*}} scopeLine: 13 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '8' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 10, Column: 16 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-zero-init +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '69' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '70' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 15, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-zero-init +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'call (LLVM IR ''call'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 9 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '58' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 12 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c new file mode 100644 index 0000000000000..91b826b379791 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c @@ -0,0 +1,210 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n, unsigned idx) { + array[idx] = 42; +} + +int main(int argc, char **argv) { + unsigned n = argc; + int * a; + foo(a, n, argc - 1); +} + +// IR-LABEL: @foo +// IR: entry +// IR-NEXT: %[[COUNT_EXT:[a-z0-9.]+]] = zext i32 {{.*}} to i64, !dbg ![[LOC_10_5:[0-9]+]] +// IR-NEXT: %[[UPPER_BOUND:[a-z0-9.]+]] = getelementptr inbounds i32, ptr %[[ARR:[a-z0-9.]+]], i64 %[[COUNT_EXT]], !dbg ![[LOC_10_5]] +// IR-NEXT: %[[IDX_EXT:[a-z0-9.]+]] = zext i32 {{.*}} to i64, !dbg ![[LOC_10_16:[0-9]+]] +// IR-NEXT: %[[PTR:[a-z0-9.]+]] = getelementptr i32, ptr %[[ARR]], i64 %[[IDX_EXT]], !dbg ![[LOC_10_16]] +// IR-NEXT: %[[UPPER_CHECK:[a-z0-9.]+]] = icmp ult ptr %[[PTR]], %[[UPPER_BOUND]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR-NEXT: %[[LOWER_CHECK:[a-z0-9.]+]] = icmp uge ptr %[[PTR]], %[[ARR]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// TODO: The condition and branch should also be ANNOT_LT_UB_OR_GT_LB (rdar://109089053) +// IR-NEXT: %[[COND:[a-z0-9.]+]] = and i1 %[[UPPER_CHECK]], %[[LOWER_CHECK]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: br i1 %{{[a-z.0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB_OR_GE_LB:[0-9]+]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR-NEXT: store {{.*}} !dbg ![[LOC_10_16]] +// IR-NEXT: ret + +// IR-LABEL: @main +// IR: entry +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]] + +// IR-DAG: ![[LOC_10_5]] = !DILocation(line: 10, column: 5 +// IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16 +// IR-DAG: ![[ANNOT_LT_UB]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// IR-DAG: ![[ANNOT_GE_LB]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LT_UB_OR_GE_LB]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} + +// opt-rem checks autogenerated by gen-opt-remarks-check-lines.py + +// OPT-REM: --- !Passed +// OPT-REM-NEXT: Pass: inline +// OPT-REM-NEXT: Name: Inlined +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: '''' +// OPT-REM-NEXT: - Callee: foo +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: - String: ''' inlined into ''' +// OPT-REM-NEXT: - Caller: main +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: - String: '''' +// OPT-REM-NEXT: - String: ' with ' +// OPT-REM-NEXT: - String: '(cost=' +// OPT-REM-NEXT: - Cost: '-15' +// OPT-REM-NEXT: - String: ', threshold=' +// OPT-REM-NEXT: - Threshold: '225' +// OPT-REM-NEXT: - String: ')' +// OPT-REM-NEXT: - String: ' at callsite ' +// OPT-REM-NEXT: - String: main +// OPT-REM-NEXT: - String: ':' +// OPT-REM-NEXT: - Line: '3' +// OPT-REM-NEXT: - String: ':' +// OPT-REM-NEXT: - Column: '5' +// OPT-REM-NEXT: - String: ';' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 10, Column: 16 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: trap (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''unreachable'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c new file mode 100644 index 0000000000000..2baa2878dfcd3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c @@ -0,0 +1,425 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n) { + array[6] = 42; +} + +int main() { + unsigned n = 5; + int * a; + foo(a, n); +} + +// IR-LABEL: @foo +// ... +// IR: icmp ult {{.*}} !dbg ![[LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_14]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// IR-NEXT: unreachable, !dbg ![[LT_TRAP_LOC_10_14]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR: br i1 %{{[0-9]+}}, label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP2:[a-z0-9]+]], !dbg ![[LOC_10_14]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// ... +// IR: [[FOO_LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[GT_TRAP_LOC_10_14:[0-9]+]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: unreachable, !dbg ![[GT_TRAP_LOC_10_14]], !annotation ![[ANNOT_GE_LB]] + + +// IR-LABEL: @main +// IR: entry +// ... +// IR-DAG: call void @llvm.memset{{.*}}, !annotation ![[ANNOT_AUTO_INIT:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %entry ], [ %[[WIDTH_CHECK_RES]], %[[MAIN_LABEL_CONT2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_AUTO_INIT]] = !{!"bounds-safety-zero-init"} + +// IR-DAG: ![[LOC_10_14]] = !DILocation(line: 10, column: 14{{.*}}) +// IR-DAG: ![[LT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) +// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[GT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[GT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) +// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 13, {{.*}} scopeLine: 13 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '8' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 10, Column: 14 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-zero-init +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '69' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '70' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 15, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-zero-init +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'call (LLVM IR ''call'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 9 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '58' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 12 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c new file mode 100644 index 0000000000000..a96af18df7097 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c @@ -0,0 +1,160 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n) { + array[6] = 42; +} + +int main() { + unsigned n = 5; + int * a; + foo(a, n); +} + +// IR-LABEL: @foo +// ... +// IR-DAG: icmp ult ptr {{.*}}, !dbg {{.+}}, !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR-DAG: icmp uge ptr {{%.*}}, !dbg {{.+}}, !annotation ![[ANNOT_GE_LB:[0-9]+]] +// IR: [[OR_COND:%.*]] = and i1 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}, !dbg {{.+}}, !annotation ![[ANNOT_GE_LB]] +// IR: br i1 [[OR_COND]], label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg {{.+}}, !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB_AND_GE_LB:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_10_14]], !annotation ![[ANNOT_LT_UB_AND_GE_LB]] + + +// IR-LABEL: @main +// ... +// IR: tail call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_0:[0-9]+]], !annotation ![[ANNOT_LE_UB_AND_CONV_TO_COUNT:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_0]], !annotation ![[ANNOT_LE_UB_AND_CONV_TO_COUNT]] + + +// IR-DAG: ![[ANNOT_LT_UB]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// IR-DAG: ![[ANNOT_GE_LB]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LT_UB_AND_GE_LB]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LE_UB_AND_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 10, Column: 14 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: trap (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c new file mode 100644 index 0000000000000..f0db1c742b44a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c @@ -0,0 +1,79 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int count_const = 0; + int * __counted_by(count_const) buff_const = 0; + count_const = 5; + buff_const = buff_const; + + int count_rt = argc; + int * __counted_by(count_rt) buff_rt = 0; + count_rt = argc; + buff_rt = buff_rt; +} + +// IR: define{{.*}} i32 @main +// IR-NEXT: entry: +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_0:[0-9]+]], !annotation ![[TRAP_REASON:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_0]], !annotation ![[TRAP_REASON]] + + +// IR-DAG: ![[TRAP_REASON]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c new file mode 100644 index 0000000000000..ee1b9a150c2a2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c @@ -0,0 +1,347 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int count_rt = argc; + int * __counted_by(count_rt) buff_rt = 0; + count_rt = argc; + buff_rt = buff_rt; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr noundef %argv) +// IR: store i32 %argc, ptr %[[ARGC_ALLOCA:[a-z0-9.]+]] +// IR: %[[ARGC_LOAD1:[a-z0-9.]+]] = load i32, ptr %[[ARGC_ALLOCA]], align 4, !dbg ![[LOC_10_20:[0-9]+]] +// IR: store i32 %[[ARGC_LOAD1]], ptr %[[COUNT_RT_ALLOCA:[a-z0-9._]+]], align 4, !dbg ![[LOC_10_9:[0-9]+]] +// IR: %[[COUNT_RT_LOAD:[a-z0-9._]+]] = load i32, ptr %[[COUNT_RT_ALLOCA]], align 4, !dbg ![[LOC_12_16:[0-9]+]] +// IR: icmp eq i32 %[[COUNT_RT_LOAD]], 0, !dbg ![[LOC_11_44:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE:[a-z0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:cont]], label %[[LABEL_TRAP:trap]], !dbg ![[LOC_11_44]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_44:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_44]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: %[[ARGC_LOAD2:[a-z0-9.]+]] = load i32, ptr %[[ARGC_ALLOCA]], align 4, !dbg ![[LOC_12_16:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_12_14:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:.+]], label %[[MAIN_LABEL_TRAP_RES:.+]], !dbg ![[LOC_12_14]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_12_14]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:.+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_12_5:[0-9]+]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp sle {{.*}} !dbg ![[LOC_12_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_EMPTY:.+]], label %[[MAIN_LABEL_TRAP_RES2:.*]], !dbg ![[LOC_12_5]] + +// IR: [[MAIN_LABEL_EMPTY]]: +// IR: %[[ARGC_CMP:[a-z0-9_]+]] = icmp sle i32 0, %[[ARGC_LOAD2]], !dbg ![[LOC_12_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES2]] + +// IR: [[MAIN_LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT2]] ], [ %[[ARGC_CMP]], %[[MAIN_LABEL_EMPTY]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %[[LABEL_CONT]] ], [ %[[TRAP_RES2]], %[[MAIN_LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_12_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_12_14:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_12_14]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[LOC_10_9]] = !DILocation(line: 10, column: 9 +// IR-DAG: ![[LOC_10_20]] = !DILocation(line: 10, column: 20 +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// IR-DAG: ![[LOC_12_5]] = !DILocation(line: 12, column: 5 +// IR-DAG: ![[LOC_12_16]] = !DILocation(line: 12, column: 16 + +// IR-DAG: ![[LOC_12_14]] = !DILocation(line: 12, column: 14 +// IR-DAG: ![[TRAP_LOC_12_14]] = !DILocation(line: 0, scope: ![[TRAP_INFO_BNDS_CHECK_FAILED:[0-9]+]], inlinedAt: ![[LOC_12_5]]) +// IR-DAG: ![[TRAP_INFO_BNDS_CHECK_FAILED]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" +// IR-DAG: ![[TRAP_LOC_11_44]] = !DILocation(line: 0, scope: ![[TRAP_INFO_BNDS_CHECK_FAILED]], inlinedAt: ![[LOC_11_44]]) + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[NEW_COUNT_POSITIVE]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''load'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 44 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 15 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '24' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '15' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 14 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 16 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''sext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c new file mode 100644 index 0000000000000..7d9404a5745ca --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c @@ -0,0 +1,313 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(void) { + int count_const = 0; + int * __counted_by(count_const) buff_const = 0; + count_const = 5; + buff_const = buff_const; +} + +// IR: define{{.*}} i32 @main{{.*}} +// IR: load {{.*}} !dbg ![[LOC_11_11:[0-9]+]] +// IR: icmp eq {{.*}} 0, !dbg ![[LOC_11_37:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE:[a-z0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:cont]], label %[[LABEL_TRAP:trap]], !dbg ![[LOC_11_37]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_37:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_37]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:.+]], label %[[MAIN_LABEL_TRAP_RES:.+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:.+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_12_5:[0-9]+]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp sle {{.*}} !dbg ![[LOC_12_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_EMPTY:.+]], label %[[MAIN_LABEL_TRAP_RES2:.*]], !dbg ![[LOC_12_5]] + +// IR: [[MAIN_LABEL_EMPTY]]: +// IR: br label %[[MAIN_LABEL_TRAP_RES2]] + +// IR: [[MAIN_LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT2]] ], [ true, %[[MAIN_LABEL_EMPTY]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %[[LABEL_CONT]] ], [ %[[TRAP_RES2]], %[[MAIN_LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_12_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 12, column: 17 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_12_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[NEW_COUNT_POSITIVE]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[LOC_12_5]] = !DILocation(line: 12, column: 5 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '51' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '51' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''load'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 50 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 18 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '24' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '14' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 17 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c new file mode 100644 index 0000000000000..440afad4850ab --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c @@ -0,0 +1,70 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple arm64e-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -O2 -triple arm64e-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +void bar(void *__sized_by(len) buf, int len); + +static void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} + +// CHECK-LABEL: @oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void oob(void) { + int arr[10]; + foo(arr, 10, 10); +} + +// CHECK-LABEL: @oob2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR_I:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH_I]], [[ADD_PTR_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34_NOT_I:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH_I]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_NOT76_I:%.*]] = and i1 [[CMP_NOT_I]], [[CMP34_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_NOT76_I]], label [[FOO_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: foo.exit: +// CHECK-NEXT: call void @bar(ptr noundef [[BOUND_PTR_ARITH_I]], i32 noundef 4) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void oob2(void) { + int arr[10]; + foo(arr, 10, -1); +} + + +// CHECK-LABEL: @oob3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void oob3(void) { + int arr[10]; + foo(arr, 10, 11); +} + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 36, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[BOUND_PTR_ARITH_I]], i32 noundef 4) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void good(void) { + int arr[10]; + foo(arr, 10, 9); +} diff --git a/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c new file mode 100644 index 0000000000000..975d0c84d6301 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c @@ -0,0 +1,181 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -O0 -triple arm64e-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +void bar(void *__sized_by(len) buf, int len); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ELEMS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP68:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ELEMS:%.*]], ptr [[ELEMS_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 [[IDX:%.*]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ELEMS_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END67:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 4, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs66: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.end67: +// CHECK-NEXT: [[TMP28:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP27]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP28]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP68]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR70]], i32 noundef 4) +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} diff --git a/clang/test/BoundsSafety/CodeGen/pointer-bounds.c b/clang/test/BoundsSafety/CodeGen/pointer-bounds.c new file mode 100644 index 0000000000000..31dc3967d5af0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pointer-bounds.c @@ -0,0 +1,62 @@ + +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +#include + +int garray[10]; +int *const __bidi_indexable lower = &garray[0]; +int *const __bidi_indexable mid = &garray[5]; +int *const __bidi_indexable upper = &garray[10]; + +#define ASSERT(X) if (!(X)) __builtin_trap(); + +int testGarray() { + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testGarray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 + +int testArray() { + int array[10]; + int *lower = &array[0]; + int *mid = &array[5]; + int *upper = &array[10]; + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testArray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 + +int testStaticArray() { + static int array[10]; + static int *const __bidi_indexable lower = &array[0]; + static int *const __bidi_indexable mid = &array[5]; + static int *const __bidi_indexable upper = &array[10]; + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testStaticArray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c b/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c new file mode 100644 index 0000000000000..5fe574a5dfef9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include +#include + +char buf[42]; + +// CHECK: @buf_ptr = global i64 ptrtoint (ptr @buf to i64), align 8 +uintptr_t buf_ptr = (uintptr_t)buf; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[I_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[BI_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[I]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[I]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[I_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: br label [[_S:%.*]] +// CHECK: _s: +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = ptrtoint ptr [[TMP2]] to i64 +// CHECK-NEXT: store i64 [[TMP3]], ptr [[S_PTR]], align 8 +// CHECK-NEXT: br label [[_I:%.*]] +// CHECK: _i: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[I]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// CHECK-NEXT: store i64 [[TMP4]], ptr [[I_PTR]], align 8 +// CHECK-NEXT: br label [[_BI:%.*]] +// CHECK: _bi: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BI:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR3]] to i64 +// CHECK-NEXT: store i64 [[TMP5]], ptr [[BI_PTR]], align 8 +// CHECK-NEXT: ret void +// +void foo(int *__single s, int *__indexable i, int *__bidi_indexable bi) { +_s:; + uintptr_t s_ptr = (uintptr_t)s; +_i:; + uintptr_t i_ptr = (uintptr_t)i; +_bi:; + uintptr_t bi_ptr = (uintptr_t)bi; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c b/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c new file mode 100644 index 0000000000000..ec6e784b62dca --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c @@ -0,0 +1,57 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -triple arm64e -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64e -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + char *ptr; + int i; + long l; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDX_NEG:%.*]] = sub nsw i64 0, [[IDX_EXT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_NEG]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH5]], [[ADD_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH5]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT6:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: store i32 3, ptr [[BOUND_PTR_ARITH5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo(int *buf __counted_by(len), int len) { + int *ptr = len + buf; + ptr = buf - len; + *ptr = 3;; +} + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[A]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR3_I:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH5_I:%.*]] = getelementptr i8, ptr [[A]], i64 -40 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH5_I]], [[ADD_PTR3_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH5_I]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[FOO_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: foo.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[A]]) #[[ATTR4]] +// CHECK-NEXT: ret i32 0 +// +int main() { + int a[10]; + foo(a, 10); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptr-arith.c b/clang/test/BoundsSafety/CodeGen/ptr-arith.c new file mode 100644 index 0000000000000..233809641dcad --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptr-arith.c @@ -0,0 +1,142 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + char *ptr; + int i; + long l; +}; + +// +__attribute__((noinline)) +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[IDXPROM4:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[IDX_NEG:%.*]] = sub i64 0, [[IDXPROM4]] +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP22]], i64 [[IDX_NEG]] +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP31:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP31]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: store i32 3, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *buf __counted_by(len), int len) { + int *ptr = len + buf; + ptr = buf - len; + *ptr = 3;; +} + +// +// XXX: Room for optimizing O0 codegen + + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[A:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 10) +// CHECK-NEXT: ret i32 0 +// +int main() { + int a[10]; + foo(a, 10); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c b/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c new file mode 100644 index 0000000000000..0686d2c02e379 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c @@ -0,0 +1,57 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ --version 3 +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix NO_FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-returns -fptrauth-calls -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix NO_FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC_NO_BOUNDS_SAFETY %s +// RUN: %clang_cc1 -triple arm64e -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -x objective-c -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC_NO_BOUNDS_SAFETY %s + + + + +#include +#include + +typedef void(*f_t)(void); + +f_t __unsafe_indexable bar(void); + +// NO_FPTR_DISC-LABEL: define dso_local ptr @foo +// NO_FPTR_DISC-SAME: () #[[ATTR0:[0-9]+]] { +// NO_FPTR_DISC-NEXT: entry: +// NO_FPTR_DISC-NEXT: [[CALL:%.*]] = call ptr @bar() +// NO_FPTR_DISC-NEXT: ret ptr [[CALL]] +// +// FPTR_DISC-LABEL: define dso_local ptr @foo +// FPTR_DISC-SAME: () #[[ATTR0:[0-9]+]] { +// FPTR_DISC-NEXT: entry: +// FPTR_DISC-NEXT: [[CALL:%.*]] = call ptr @bar() +// FPTR_DISC-NEXT: [[TMP0:%.*]] = icmp ne ptr [[CALL]], null +// FPTR_DISC-NEXT: br i1 [[TMP0]], label [[RESIGN_NONNULL:%.*]], label [[RESIGN_CONT:%.*]] +// FPTR_DISC: resign.nonnull: +// FPTR_DISC-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[CALL]] to i64 +// FPTR_DISC-NEXT: [[TMP2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[TMP1]], i32 0, i64 18983, i32 0, i64 0) +// FPTR_DISC-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr +// FPTR_DISC-NEXT: br label [[RESIGN_CONT]] +// FPTR_DISC: resign.cont: +// FPTR_DISC-NEXT: [[TMP4:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[TMP3]], [[RESIGN_NONNULL]] ] +// FPTR_DISC-NEXT: ret ptr [[TMP4]] +// +// FPTR_DISC_NO_BOUNDS_SAFETY-LABEL: define dso_local ptr @foo +// FPTR_DISC_NO_BOUNDS_SAFETY-SAME: () #[[ATTR0:[0-9]+]] { +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: entry: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[CALL:%.*]] = call ptr @bar() +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP0:%.*]] = icmp ne ptr [[CALL]], null +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: br i1 [[TMP0]], label [[RESIGN_NONNULL:%.*]], label [[RESIGN_CONT:%.*]] +// FPTR_DISC_NO_BOUNDS_SAFETY: resign.nonnull: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[CALL]] to i64 +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[TMP1]], i32 0, i64 18983, i32 0, i64 0) +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: br label [[RESIGN_CONT]] +// FPTR_DISC_NO_BOUNDS_SAFETY: resign.cont: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP4:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[TMP3]], [[RESIGN_NONNULL]] ] +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: ret ptr [[TMP4]] +// +const char *foo(void) { + return __unsafe_forge_null_terminated(char *, bar()); +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c new file mode 100644 index 0000000000000..1fcb4bd3ed8f4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c @@ -0,0 +1,574 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" +// +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +// XFAIL: !rdar109424213 + +#include +#include + +int a[4]; +int b[4]; +int c[4]; +int d[4]; + +// CHECK-LABEL: @read_from_global_array_can_remove_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @a, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_1:%.*]] = add nsw i32 [[TMP1]], [[TMP0]] +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_2:%.*]] = add nsw i32 [[TMP2]], [[ADD_1]] +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_3:%.*]] = add nsw i32 [[TMP3]], [[ADD_2]] +// CHECK-NEXT: ret i32 [[ADD_3]] +// +int read_from_global_array_can_remove_checks() { + int res = 0; + for (unsigned char i = 0; i < 4; i++) { + res += a[i]; + } + return res; +} + +// CHECK-LABEL: @read_from_global_array_trap_last_iter( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +int read_from_global_array_trap_last_iter() { + int res = 0; + for (unsigned char i = 0; i < 5; i++) { + res += a[i]; + } + return res; +} + +// CHECK-LABEL: @read_from_global_array_cannot_rename( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[RES_09:%.*]] = phi i32 [ [[ADD]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_08:%.*]] = phi i8 [ [[INC:%.*]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[I_08]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], getelementptr inbounds ([4 x i32], ptr @a, i64 1, i64 0), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], @a, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP3]], [[RES_09]] +// CHECK-NEXT: [[INC]] = add nuw nsw i8 [[I_08]], 1 +// CHECK-NEXT: [[CONV:%.*]] = zext i8 [[INC]] to i32 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[CONV]], [[N]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP]], {{!llvm.loop ![0-9]+}} +// +int read_from_global_array_cannot_rename(unsigned n) { + int res = 0; + for (unsigned char i = 0; i < n; i++) { + res += a[i]; + } + return res; +} + +typedef struct { + uint16_t len; + uint16_t offset; + uint8_t payload[__counted_by(offset + len)]; +} hdr_t; + +// CHECK-LABEL: @concat_to_separate_clobals( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND156:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr @a, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV79]], ptr @b, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV100]], ptr @c, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV121]], ptr @d, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND156_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_1]], label [[CONT_1:%.*]], label [[TRAP]] +// CHECK: cont.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP10:%.*]] = load i8, ptr [[TMP9]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP10]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP12:%.*]] = load i8, ptr [[TMP11]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_1:%.*]] = zext i8 [[TMP12]] to i32 +// CHECK-NEXT: store i32 [[CONV79_1]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP13]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV100_1]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_1:%.*]] = zext i8 [[TMP15]] to i32 +// CHECK-NEXT: store i32 [[CONV121_1]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND156_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_2]], label [[CONT_2:%.*]], label [[TRAP]] +// CHECK: cont.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[TMP18]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_2:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV79_2]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP21:%.*]] = load i8, ptr [[TMP20]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_2:%.*]] = zext i8 [[TMP21]] to i32 +// CHECK-NEXT: store i32 [[CONV100_2]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV121_2]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND156_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_3]], label [[CONT_3:%.*]], label [[TRAP]] +// CHECK: cont.3: +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP24:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP24]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP26:%.*]] = load i8, ptr [[TMP25]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_3:%.*]] = zext i8 [[TMP26]] to i32 +// CHECK-NEXT: store i32 [[CONV79_3]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_3:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV100_3]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_3:%.*]] = zext i8 [[TMP29]] to i32 +// CHECK-NEXT: store i32 [[CONV121_3]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void concat_to_separate_clobals(hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + for (unsigned char i = 0; i < 4; i++) { + uint8_t *__counted_by(4) p = params; + a[i] = p[3]; // checks not removed for p[] + b[i] = p[1]; // checks not removed for b[] and p[] + c[i] = p[2]; // checks not removed for c[] and p[] + d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +struct arrays { + int a[4]; + int b[4]; + int c[4]; + int d[4]; +}; + +// CHECK-LABEL: @concat_to_arrays_struct_can_remove_arrays_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT138:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont138: +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS:%.*]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1 +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[ARRAYS]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[B]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[UPPER83]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[UPPER106]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND166_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_1]], label [[CONT138_1:%.*]], label [[TRAP]] +// CHECK: cont138.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = load i8, ptr [[TMP9]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP11]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr [[TMP10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP12]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV80_1]], ptr [[TMP13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_1:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV103_1]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_1:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV126_1]], ptr [[TMP18]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND166_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_2]], label [[CONT138_2:%.*]], label [[TRAP]] +// CHECK: cont138.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 2 +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[TMP20]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr [[TMP21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 2 +// CHECK-NEXT: [[TMP25:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_2:%.*]] = zext i8 [[TMP25]] to i32 +// CHECK-NEXT: store i32 [[CONV80_2]], ptr [[TMP24]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 2 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_2:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV103_2]], ptr [[TMP26]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 2 +// CHECK-NEXT: [[TMP30:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_2:%.*]] = zext i8 [[TMP30]] to i32 +// CHECK-NEXT: store i32 [[CONV126_2]], ptr [[TMP29]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND166_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_3]], label [[CONT138_3:%.*]], label [[TRAP]] +// CHECK: cont138.3: +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 3 +// CHECK-NEXT: [[TMP33:%.*]] = load i8, ptr [[TMP31]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP33]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr [[TMP32]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 3 +// CHECK-NEXT: [[TMP36:%.*]] = load i8, ptr [[TMP34]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_3:%.*]] = zext i8 [[TMP36]] to i32 +// CHECK-NEXT: store i32 [[CONV80_3]], ptr [[TMP35]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 3 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP39:%.*]] = load i8, ptr [[TMP38]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_3:%.*]] = zext i8 [[TMP39]] to i32 +// CHECK-NEXT: store i32 [[CONV103_3]], ptr [[TMP37]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 3 +// CHECK-NEXT: [[TMP41:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_3:%.*]] = zext i8 [[TMP41]] to i32 +// CHECK-NEXT: store i32 [[CONV126_3]], ptr [[TMP40]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void concat_to_arrays_struct_can_remove_arrays_check(struct arrays *arrays, hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < 4; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +// CHECK-LABEL: @concat_to_arrays_struct_trap_on_last_iter( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT138:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont138: +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS:%.*]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 4 +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[ARRAYS]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[UPPER]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[UPPER83]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[UPPER106]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND166_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_1]], label [[CONT138_1:%.*]], label [[TRAP]] +// CHECK: cont138.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP11:%.*]] = load i8, ptr [[TMP10]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP11]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr [[TMP9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 5 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP12]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV80_1]], ptr [[TMP13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_1:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV103_1]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_1:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV126_1]], ptr [[TMP18]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND166_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_2]], label [[CONT138_2:%.*]], label [[TRAP]] +// CHECK: cont138.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 2 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[TMP21]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr [[TMP20]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 6 +// CHECK-NEXT: [[TMP25:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_2:%.*]] = zext i8 [[TMP25]] to i32 +// CHECK-NEXT: store i32 [[CONV80_2]], ptr [[TMP24]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 2 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_2:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV103_2]], ptr [[TMP26]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 2 +// CHECK-NEXT: [[TMP30:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_2:%.*]] = zext i8 [[TMP30]] to i32 +// CHECK-NEXT: store i32 [[CONV126_2]], ptr [[TMP29]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND166_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_3]], label [[CONT138_3:%.*]], label [[TRAP]] +// CHECK: cont138.3: +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 3 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP33:%.*]] = load i8, ptr [[TMP32]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP33]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr [[TMP31]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 7 +// CHECK-NEXT: [[TMP36:%.*]] = load i8, ptr [[TMP34]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_3:%.*]] = zext i8 [[TMP36]] to i32 +// CHECK-NEXT: store i32 [[CONV80_3]], ptr [[TMP35]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 3 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP39:%.*]] = load i8, ptr [[TMP38]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_3:%.*]] = zext i8 [[TMP39]] to i32 +// CHECK-NEXT: store i32 [[CONV103_3]], ptr [[TMP37]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 3 +// CHECK-NEXT: [[TMP41:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_3:%.*]] = zext i8 [[TMP41]] to i32 +// CHECK-NEXT: store i32 [[CONV126_3]], ptr [[TMP40]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void concat_to_arrays_struct_trap_on_last_iter(struct arrays *arrays, hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < 5; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +// CHECK-LABEL: @concat_to_arrays_struct_cannot_remove_arrays_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[CMP168_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP168_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYS:%.*]], i64 4 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER129:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[PARAMS_SROA_0_0170:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[FOR_BODY_LR_PH]] ], [ [[ADD_PTR50:%.*]], [[CONT138:%.*]] ] +// CHECK-NEXT: [[I_0169:%.*]] = phi i8 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[CONT138]] ] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[PARAMS_SROA_0_0170]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PARAMS_SROA_0_0170]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR50]] = getelementptr inbounds i8, ptr [[PARAMS_SROA_0_0170]], i64 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[I_0169]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[ARRAYS]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND141:%.*]] = and i1 [[TMP3]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND141]], label [[CONT69:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont69: +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 3 +// CHECK-NEXT: [[TMP6:%.*]] = load i8, ptr [[TMP5]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP6]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[TMP2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i32, ptr [[UPPER]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP9:%.*]] = load i8, ptr [[TMP7]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP9]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[TMP8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[UPPER83]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[TMP10]], [[UPPER106]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[TMP10]], [[UPPER83]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND145:%.*]] = and i1 [[TMP11]], [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND145]], label [[CONT115:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont115: +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 2 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP13]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[TMP10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i32, ptr [[UPPER106]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[TMP15]], [[UPPER129]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[TMP15]], [[UPPER106]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND147:%.*]] = and i1 [[TMP16]], [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND147]], label [[CONT138]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont138: +// CHECK-NEXT: [[TMP18:%.*]] = load i8, ptr [[PARAMS_SROA_0_0170]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP18]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add i8 [[I_0169]], 1 +// CHECK-NEXT: [[CONV4:%.*]] = zext i8 [[INC]] to i32 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[CONV4]], [[N]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP]], {{!llvm.loop ![0-9]+}} +// +void concat_to_arrays_struct_cannot_remove_arrays_check(struct arrays *arrays, hdr_t *p_buf, unsigned n) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < n; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..41db2a09edb31 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c @@ -0,0 +1,727 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -fno-split-cold-code -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -fno-split-cold-code -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include + +// All accesses in the loop at in bounds and can be eliminated. +// CHECK-LABEL: @loop_all_accesses_in_bounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 4 [[DST:%.*]], i8 0, i64 [[TMP1]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_access_by_dereference( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[N]], 0 +// CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[R_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: ret i32 [[R_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[B_SROA_0_09:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT1]] ], [ [[A]], [[ENTRY]] ] +// CHECK-NEXT: [[R_08:%.*]] = phi i32 [ [[ADD]], [[CONT1]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_07:%.*]] = phi i32 [ [[INC:%.*]], [[CONT1]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[B_SROA_0_09]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[B_SROA_0_09]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[B_SROA_0_09]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[B_SROA_0_09]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP2]], [[R_08]] +// CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_07]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int loop_access_by_dereference(int *__counted_by(n) a, int n) { + int *b = a; + int r = 0; + for (int i = 0; i < n; ++i) { + r += *b++; + } + return r; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp ult i32 [[START:%.*]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], [[N]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_1(int* __counted_by(n) dst, + unsigned n, unsigned start) { + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_2_add( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[START:%.*]], 10 +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = add i32 [[N]], -11 +// CHECK-NEXT: [[TMP3:%.*]] = sub i32 [[TMP2]], [[START]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_2_add(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start + 10; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_3_modulo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = urem i32 [[START:%.*]], [[N]] +// CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP2]], [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[INDVARS_IV_NEXT]], [[TMP0]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_variable_start_3_modulo(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start % n; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_4_div( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[START2:%.*]], 10 +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[DIV]], [[START1:%.*]] +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START1]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], [[N]] +// CHECK-NEXT: [[TMP4:%.*]] = sub i32 [[TMP3]], [[DIV]] +// CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP6:%.*]] = shl nuw nsw i64 [[TMP5]], 2 +// CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP6]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP7]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_4_div(int* __counted_by(n) dst, + unsigned n, unsigned start1, + unsigned start2) { + start1 += (start2 / 10); + for (unsigned i = start1; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] += 1; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_length_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_07:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[I_07]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add nuw i64 [[I_07]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INC]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_length_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i] += 1; +} + +// The lower bound checks can be eliminated, because we know that: +// 1. computing dst + n does not wrap +// 2. i + 1 <= n from loop bound. +// +// 1. and 2. together imply that dst + i + 1 does not wrap, hence dst + i + 1 >= dst is true. + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_07:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[ADD]] = add nuw i64 [[I_07]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[ADD]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// No checks can be eliminated, as dst + i + 2 may wrap and is out of bounds. +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len(int* __counted_by(n) dst, int n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[N]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[N]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_08:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD3:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[I_08]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD3]] = add nuw i64 [[I_08]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD3]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len(int* __counted_by(n) dst, unsigned long long n) { + for (unsigned long long i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// Both lower checks can be eliminated. +// dst + i + 1 >= dst can be eliminated because i + 1 <= n and dst + n does not wrap. +// +// dst + i + 2 >= dst can be eliminated because from the check for dst[i+1] we know: +// 1. dst + n does not wrap. +// 2. dst + i + 1 < dst + n +// +// 1. and 2. together imply dst + i + 1 does not wrap, and dst + i + 2 also +// does not (note the < in 2.). Hence dst + i + 2 >= dst is true. +// +// FIXME: Regressed at the moment, rdar://120485098. +// CHECK-LABEL: @loop_accesses_eliminate_second_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP24_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP24_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT15:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND17:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND17]], label [[CONT15]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_second_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) { + dst[i+1] = 0; + dst[i+2] = 0; + } +} + +// We can eliminate the checks for dst[i+1], because the earlier checks for +// dst[i+2] make them redundant. +// CHECK-LABEL: @loop_accesses_eliminate_later_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT15:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX9:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX9]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX9]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND17:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND17]], label [[CONT15]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 2000 +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_later_checks(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < 2000; i += 1) { + dst[i+2] = 0; + dst[i+1] = 0; + } +} + +// The checks for dst[4], dst[3] and dst[2] should be removed, because they are +// redundant after checking dst[5]. +// CHECK-LABEL: @elim_consecutive_writes( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[DST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX7:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX7]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX7]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND50:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND50]], label [[CONT49:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont49: +// CHECK-NEXT: [[ARRAYIDX43:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(16) [[ARRAYIDX43]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void elim_consecutive_writes(int* __counted_by(n) dst, unsigned n) { + dst[1] = 0; // Need to check for wrap and against upper bound. + dst[5] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. +} + +// CHECK-LABEL: @elim_consecutive_writes2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[IDX:%.*]], 3 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT37:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont37: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX7:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX19:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX19]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX31:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX31]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX43:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX43]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX43]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND53:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND53]], label [[CONT49:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont49: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX43]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[IF_END]] +// CHECK: if.end: +// CHECK-NEXT: ret void +// +void elim_consecutive_writes2(int* __counted_by(n) dst, unsigned n, unsigned idx) { + if (idx >= 4) { + dst[idx] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. + dst[5] = 0; // Need to check against upper bound. + } +} + +// TODO +// CHECK-LABEL: @count_ptr_relations( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[LAND_RHS:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[N]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARG:%.*]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = add nuw nsw i64 [[IDX_EXT]], 4611686018427387903 +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = and i64 [[TMP0]], 4611686018427387903 +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ult i64 [[SUB_PTR_DIV]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP:%.*]], label [[CONT59:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont59: +// CHECK-NEXT: store i32 0, ptr [[ARG]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX53:%.*]] = getelementptr i32, ptr [[ARG]], i64 [[CONV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX53]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[SUB]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT85:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont85: +// CHECK-NEXT: store i32 1, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[SUB77:%.*]] = add i32 [[N]], -2 +// CHECK-NEXT: [[IDXPROM78:%.*]] = zext i32 [[SUB77]] to i64 +// CHECK-NEXT: [[ARRAYIDX79:%.*]] = getelementptr i32, ptr [[BOUND_PTR_ARITH]], i64 [[IDXPROM78]] +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX79]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: ret void +// +void count_ptr_relations(int *__counted_by(n) arg, unsigned n) { + if (n == 0) + return; + unsigned m = n-1; + int *__counted_by(m) ptr = arg + 1; + arg[0] = 0; + arg[n-1] = 0; + ptr[0] = 1; // eliminate checks? + ptr[m-1] = 1; // eliminate checks? +} + +// CHECK-LABEL: @ptrinc0( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5_NOT:%.*]] = icmp eq i32 [[INLEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5_NOT]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]] +// CHECK: while.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[INLEN]] to i64 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[BUF:%.*]], i8 0, i64 [[TMP0]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[WHILE_END]] +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc0(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned inc = 0; + char *currentBuf = buf; + while (inc < inLen) { + *currentBuf = 0; + inc++; + currentBuf++; + } +} + +// TODO rdar://75687730 +// CHECK-LABEL: @ptrinc1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[INLEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP_NOT4:%.*]] = icmp eq i32 [[INLEN]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT4]], label [[WHILE_END:%.*]], label [[WHILE_BODY:%.*]] +// CHECK: while.body: +// CHECK-NEXT: [[CURRENTLEN_06:%.*]] = phi i32 [ [[DEC:%.*]], [[CONT1:%.*]] ], [ [[INLEN]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CURRENTBUF_SROA_0_05:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT1]] ], [ [[BUF]], [[ENTRY]] ] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[CURRENTBUF_SROA_0_05]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[CURRENTBUF_SROA_0_05]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i8 0, ptr [[CURRENTBUF_SROA_0_05]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DEC]] = add i32 [[CURRENTLEN_06]], -1 +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[CURRENTBUF_SROA_0_05]], i64 1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[DEC]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc1(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned currentLen = inLen; + char *currentBuf = buf; + while (currentLen != 0) { + *currentBuf = 0; + currentLen--; + currentBuf++; + } +} + +// TODO: rdar://99414486 - All BoundsSafety checks should be removed. +// CHECK-LABEL: @test_reforge_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult i32 [[IDX:%.*]], [[TABLE_SIZE:%.*]] +// CHECK-NEXT: [[CMP1_NOT37:%.*]] = icmp ne i32 [[IDX]], 0 +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1_NOT37]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[FOR_BODY_PREHEADER:%.*]], label [[RETURN:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[RETURN]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TABLE:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq i32 [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[IF_THEN4:%.*]], label [[FOR_COND]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: if.then4: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr i32, ptr [[TABLE]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[IDX_EXT_LE:%.*]] = zext i32 [[TABLE_SIZE]] to i64 +// CHECK-NEXT: [[ADD_PTR_LE:%.*]] = getelementptr inbounds i32, ptr [[TABLE]], i64 [[IDX_EXT_LE]] +// CHECK-NEXT: [[DOTNOT31:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH_LE]], [[TABLE]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT31]], label [[TRAP:%.*]], label [[RETURN]], {{!annotation ![0-9]+}} +// CHECK: return: +// CHECK-NEXT: [[RETVAL_SROA_4_3:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[ADD_PTR_LE]], [[IF_THEN4]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_3:%.*]] = phi ptr [ null, [[ENTRY]] ], [ [[BOUND_PTR_ARITH_LE]], [[IF_THEN4]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[RETVAL_SROA_0_3]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[RETVAL_SROA_4_3]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test_reforge_indexable(unsigned idx, int*__counted_by(table_size) table, + unsigned table_size, int value) { + if (idx >= table_size) + return 0; + + for (unsigned i = 0; i < idx; i++) { + int *__single value_entry = &table[i]; + if (*value_entry == value) + return &table[i]; + } + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..e8dd03d26e1a1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c @@ -0,0 +1,166 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct struct_1 { + void *value; +}; + +// CHECK-LABEL: @access_struct_1_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT22_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT22_NOT]], label [[CLEANUP13:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP13]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[CLEANUP13_LOOPEXIT_SPLIT_LOOP_EXIT:%.*]], label [[FOR_COND]] +// CHECK: cleanup13.loopexit.split.loop.exit: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV]] +// CHECK-NEXT: br label [[CLEANUP13]] +// CHECK: cleanup13: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH_LE]], [[CLEANUP13_LOOPEXIT_SPLIT_LOOP_EXIT]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_all_checks_removable( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +// CHECK-LABEL: @access_struct_1_checks_needed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ], [ 0, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP13:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT1]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP1]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[FOR_COND]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CLEANUP13]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cleanup13: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_checks_needed( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i+1]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +struct struct_2 { + int a; + int b; +}; + +// CHECK-LABEL: @access_struct_2_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT36_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT36_NOT]], label [[CLEANUP24:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_2:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[TMP0]], [[A:%.*]] +// CHECK-NEXT: br i1 [[CMP3]], label [[CONT12:%.*]], label [[FOR_INC]] +// CHECK: cont12: +// CHECK-NEXT: [[B13:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14:%.*]] = icmp eq i32 [[TMP1]], [[B:%.*]] +// CHECK-NEXT: br i1 [[CMP14]], label [[CLEANUP24]], label [[FOR_INC]] +// CHECK: for.inc: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP24]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: cleanup24: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH]], [[CONT12]] ], [ null, [[FOR_INC]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_2 * access_struct_2_all_checks_removable( + struct struct_2 *__counted_by(size) src, unsigned size, int a, int b) { + for (unsigned i = 0; i < size; i++) { + struct struct_2 *value_entry = &src[i]; + if (value_entry->a == a && value_entry->b == b) + return value_entry; + } + return 0; +} + +typedef struct { + int f1; + int f2; + int f3; +} MyStruct; + +// All runtime checks in the loop can be removed. +// CHECK-LABEL: @array_of_structs_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP29_NOT:%.*]] = icmp eq i32 [[NUMITEMS:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP29_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[NUMITEMS]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD21:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[RES_031:%.*]] = phi i32 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[ADD21]], [[FOR_BODY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_MYSTRUCT:%.*]], ptr [[ITEMS:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F2:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F3:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[F3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[TMP0]], [[RES_031]] +// CHECK-NEXT: [[ADD20:%.*]] = add i32 [[ADD]], [[TMP1]] +// CHECK-NEXT: [[ADD21]] = add i32 [[ADD20]], [[TMP2]] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int array_of_structs_all_checks_removable(MyStruct *__counted_by(numItems) items, + unsigned numItems) { + int res = 0; + for (unsigned i = 0; i < numItems; i++) { + const MyStruct *ptr = &items[i]; + res += ptr->f1 + ptr->f2 + ptr->f3; + } + return res; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..0e618041dbd3e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c @@ -0,0 +1,165 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct buf { + int member; + int arr[10]; +}; + +// CHECK-LABEL: @array_member_access_can_remove( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX_1]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_can_remove(struct buf *bp) { + for (int i = 0; i < 10; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_trap_on_last_iter( +// CHECK-NEXT: cont1: +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 44 +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX_1]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1_1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// rdar://138776402 +// CHECK: cont1.1: +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX_1]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX_2]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT1_2:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.2: +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX_3]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT1_3:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.3: +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[ARRAYIDX_4]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT1_4:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.4: +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[ARRAYIDX_5]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT1_5:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.5: +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[ARRAYIDX_6]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT1_6:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.6: +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[ARRAYIDX_7]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT1_7:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.7: +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[ARRAYIDX_8]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT1_8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.8: +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[ARRAYIDX_9]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT1_9:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont1.9: +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]], {{!annotation ![0-9]+}} +// +void array_member_access_trap_on_last_iter(struct buf *bp) { + for (int i = 0; i < 11; ++i) + bp->arr[i] = i; +} + +struct buf_var_size { + unsigned size; + int arr[__counted_by(size)]; +}; + +// CHECK-LABEL: @array_member_access_can_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP14_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP]], i64 4 +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_can_remove_variable(struct buf_var_size *bp) { + for (int i = 0; i < bp->size; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_cannot_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP12_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP12_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT8:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP3:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_cannot_remove_variable(struct buf_var_size *bp, unsigned n) { + for (int i = 0; i < n; ++i) + bp->arr[i] = i; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c new file mode 100644 index 0000000000000..ca17c1c8a32d2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c @@ -0,0 +1,281 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct buf { + int member; + int arr[10]; +}; + +// rdar://128576231 +// CHECK-LABEL: @array_member_access_can_remove( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[BP]], i64 44 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// rdar://138776402 +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[BP]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP0]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_1:%.*]] = and i1 [[TMP4]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_1]], label [[CONT2_1:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.1: +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[BP]], i64 16, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYIDX_2]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_2:%.*]] = and i1 [[TMP7]], [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_2]], label [[CONT2_2:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.2: +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[BP]], i64 20, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[ARRAYIDX_3]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_3:%.*]] = and i1 [[TMP10]], [[TMP11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_3]], label [[CONT2_3:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.3: +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[BP]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[TMP12]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[ARRAYIDX_4]], [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_4:%.*]] = and i1 [[TMP13]], [[TMP14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_4]], label [[CONT2_4:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.4: +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[BP]], i64 28, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[TMP15]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[ARRAYIDX_5]], [[TMP15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_5:%.*]] = and i1 [[TMP16]], [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_5]], label [[CONT2_5:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.5: +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[BP]], i64 32, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP19:%.*]] = icmp ule ptr [[TMP18]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = icmp ule ptr [[ARRAYIDX_6]], [[TMP18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_6:%.*]] = and i1 [[TMP19]], [[TMP20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_6]], label [[CONT2_6:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.6: +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i8, ptr [[BP]], i64 36, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = icmp ule ptr [[TMP21]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = icmp ule ptr [[ARRAYIDX_7]], [[TMP21]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_7:%.*]] = and i1 [[TMP22]], [[TMP23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_7]], label [[CONT2_7:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.7: +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr i8, ptr [[BP]], i64 40, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = icmp ule ptr [[TMP24]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = icmp ule ptr [[ARRAYIDX_8]], [[TMP24]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_8:%.*]] = and i1 [[TMP25]], [[TMP26]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_8]], label [[CONT2_8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.8: +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[BP]], i64 44, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX_9]], [[TMP27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT2_9:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont2.9: +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_can_remove(struct buf *bp) { + for (int i = 0; i < 10; ++i) + bp->arr[i] = i; +} + +// rdar://128576231 +// CHECK-LABEL: @array_member_access_trap_on_last_iter( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[BP]], i64 44 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// rdar://138776402 +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[BP]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP0]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_1:%.*]] = and i1 [[TMP4]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_1]], label [[CONT2_1:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.1: +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[BP]], i64 16, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYIDX_2]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_2:%.*]] = and i1 [[TMP7]], [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_2]], label [[CONT2_2:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.2: +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[BP]], i64 20, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[ARRAYIDX_3]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_3:%.*]] = and i1 [[TMP10]], [[TMP11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_3]], label [[CONT2_3:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.3: +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[BP]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[TMP12]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[ARRAYIDX_4]], [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_4:%.*]] = and i1 [[TMP13]], [[TMP14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_4]], label [[CONT2_4:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.4: +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[BP]], i64 28, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[TMP15]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[ARRAYIDX_5]], [[TMP15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_5:%.*]] = and i1 [[TMP16]], [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_5]], label [[CONT2_5:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.5: +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[BP]], i64 32, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP19:%.*]] = icmp ule ptr [[TMP18]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = icmp ule ptr [[ARRAYIDX_6]], [[TMP18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_6:%.*]] = and i1 [[TMP19]], [[TMP20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_6]], label [[CONT2_6:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.6: +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i8, ptr [[BP]], i64 36, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = icmp ule ptr [[TMP21]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = icmp ule ptr [[ARRAYIDX_7]], [[TMP21]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_7:%.*]] = and i1 [[TMP22]], [[TMP23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_7]], label [[CONT2_7:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.7: +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr i8, ptr [[BP]], i64 40, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = icmp ule ptr [[TMP24]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = icmp ule ptr [[ARRAYIDX_8]], [[TMP24]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_8:%.*]] = and i1 [[TMP25]], [[TMP26]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_8]], label [[CONT2_8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.8: +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[BP]], i64 44, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX_9]], [[TMP27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT2_9:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont2.9: +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_10:%.*]] = getelementptr i8, ptr [[BP]], i64 44 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr i8, ptr [[BP]], i64 48, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = icmp ule ptr [[TMP28]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP30:%.*]] = icmp ule ptr [[ARRAYIDX_10]], [[TMP28]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_10:%.*]] = and i1 [[TMP29]], [[TMP30]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_10]], label [[CONT2_10:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont2.10: +// CHECK-NEXT: store i32 10, ptr [[ARRAYIDX_10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_trap_on_last_iter(struct buf *bp) { + for (int i = 0; i < 11; ++i) + bp->arr[i] = i; +} + +struct buf_var_size { + unsigned size; + int arr[__counted_by(size)]; +}; + +// rdar://128576231 +// CHECK-LABEL: @array_member_access_can_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP16_NOT:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP16_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT10:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT10]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont10: +// CHECK-NEXT: [[TMP4:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_can_remove_variable(struct buf_var_size *bp) { + for (int i = 0; i < bp->size; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_cannot_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP14_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT9:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[CONT9]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: [[TMP5:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_cannot_remove_variable(struct buf_var_size *bp, unsigned n) { + for (int i = 0; i < n; ++i) + bp->arr[i] = i; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c new file mode 100644 index 0000000000000..d586189a1c6ef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c @@ -0,0 +1,166 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct struct_1 { + void *value; +}; + +// CHECK-LABEL: @access_struct_1_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT24_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT24_NOT]], label [[CLEANUP14:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP14]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[CLEANUP14_LOOPEXIT_SPLIT_LOOP_EXIT:%.*]], label [[FOR_COND]] +// CHECK: cleanup14.loopexit.split.loop.exit: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV]] +// CHECK-NEXT: br label [[CLEANUP14]] +// CHECK: cleanup14: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH_LE]], [[CLEANUP14_LOOPEXIT_SPLIT_LOOP_EXIT]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_all_checks_removable( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +// CHECK-LABEL: @access_struct_1_checks_needed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ], [ 0, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP14:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[DOTNOT25:%.*]] = icmp ugt ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT25]], label [[TRAP:%.*]], label [[CONT1]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP1]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[FOR_COND]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CLEANUP14]], {{!annotation ![0-9]+}} +// CHECK: cleanup14: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_checks_needed( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i+1]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +struct struct_2 { + int a; + int b; +}; + +// CHECK-LABEL: @access_struct_2_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT38_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT38_NOT]], label [[CLEANUP25:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_2:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[TMP0]], [[A:%.*]] +// CHECK-NEXT: br i1 [[CMP3]], label [[CONT12:%.*]], label [[FOR_INC]] +// CHECK: cont12: +// CHECK-NEXT: [[B13:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14:%.*]] = icmp eq i32 [[TMP1]], [[B:%.*]] +// CHECK-NEXT: br i1 [[CMP14]], label [[CLEANUP25]], label [[FOR_INC]] +// CHECK: for.inc: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP25]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: cleanup25: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH]], [[CONT12]] ], [ null, [[FOR_INC]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_2 * access_struct_2_all_checks_removable( + struct struct_2 *__counted_by(size) src, unsigned size, int a, int b) { + for (unsigned i = 0; i < size; i++) { + struct struct_2 *value_entry = &src[i]; + if (value_entry->a == a && value_entry->b == b) + return value_entry; + } + return 0; +} + +typedef struct { + int f1; + int f2; + int f3; +} MyStruct; + +// All runtime checks in the loop can be removed. +// CHECK-LABEL: @array_of_structs_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP29_NOT:%.*]] = icmp eq i32 [[NUMITEMS:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP29_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[NUMITEMS]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD21:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[RES_031:%.*]] = phi i32 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[ADD21]], [[FOR_BODY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_MYSTRUCT:%.*]], ptr [[ITEMS:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F2:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F3:%.*]] = getelementptr inbounds i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[F3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[TMP0]], [[RES_031]] +// CHECK-NEXT: [[ADD20:%.*]] = add i32 [[ADD]], [[TMP1]] +// CHECK-NEXT: [[ADD21]] = add i32 [[ADD20]], [[TMP2]] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int array_of_structs_all_checks_removable(MyStruct *__counted_by(numItems) items, + unsigned numItems) { + int res = 0; + for (unsigned i = 0; i < numItems; i++) { + const MyStruct *ptr = &items[i]; + res += ptr->f1 + ptr->f2 + ptr->f3; + } + return res; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c new file mode 100644 index 0000000000000..38d1ee8d76d1a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c @@ -0,0 +1,814 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -fno-split-cold-code -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -fno-split-cold-code -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include + +// All accesses in the loop at in bounds and can be eliminated. +// CHECK-LABEL: @loop_all_accesses_in_bounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 4 [[DST:%.*]], i8 0, i64 [[TMP1]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_access_by_dereference( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sgt i32 [[N]], 0 +// CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[R_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: ret i32 [[R_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[B_SROA_0_011:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT2]] ], [ [[A]], [[ENTRY]] ] +// CHECK-NEXT: [[R_010:%.*]] = phi i32 [ [[ADD]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_09:%.*]] = phi i32 [ [[INC:%.*]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[B_SROA_0_011]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[B_SROA_0_011]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[B_SROA_0_011]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP2]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND3]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[B_SROA_0_011]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP3]], [[R_010]] +// CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_09]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int loop_access_by_dereference(int *__counted_by(n) a, int n) { + int *b = a; + int r = 0; + for (int i = 0; i < n; ++i) { + r += *b++; + } + return r; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[START:%.*]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], [[N]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_1(int* __counted_by(n) dst, + unsigned n, unsigned start) { + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_2_add( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[START:%.*]], 10 +// CHECK-NEXT: [[CMP9:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP9]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = add i32 [[N]], -11 +// CHECK-NEXT: [[TMP3:%.*]] = sub i32 [[TMP2]], [[START]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_2_add(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start + 10; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_3_modulo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = urem i32 [[START:%.*]], [[N]] +// CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP2]], [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[INDVARS_IV_NEXT]], [[TMP0]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_variable_start_3_modulo(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start % n; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_4_div( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[START2:%.*]], 10 +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[DIV]], [[START1:%.*]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP9]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START1]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], [[N]] +// CHECK-NEXT: [[TMP4:%.*]] = sub i32 [[TMP3]], [[DIV]] +// CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP6:%.*]] = shl nuw nsw i64 [[TMP5]], 2 +// CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP6]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP7]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_4_div(int* __counted_by(n) dst, + unsigned n, unsigned start1, + unsigned start2) { + start1 += (start2 / 10); + for (unsigned i = start1; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] += 1; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_length_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_09:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[I_09]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add nuw i64 [[I_09]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INC]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_length_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i] += 1; +} + +// The lower bound checks can be eliminated, because we know that: +// 1. computing dst + n does not wrap +// 2. i + 1 <= n from loop bound. +// +// 1. and 2. together imply that dst + i + 1 does not wrap, hence dst + i + 1 >= dst is true. + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_09:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[CONT3:%.*]] ] +// CHECK-NEXT: [[ADD]] = add nuw i64 [[I_09]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[ADD]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT3]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// No checks can be eliminated, as dst + i + 2 may wrap and is out of bounds. +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8:%.*]] = icmp sgt i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len(int* __counted_by(n) dst, int n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP9_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[N]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[N]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_010:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD4:%.*]], [[CONT3:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[I_010]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND5:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND5]], label [[CONT3]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD4]] = add nuw i64 [[I_010]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD4]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len(int* __counted_by(n) dst, unsigned long long n) { + for (unsigned long long i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// Both lower checks can be eliminated. +// dst + i + 1 >= dst can be eliminated because i + 1 <= n and dst + n does not wrap. +// +// dst + i + 2 >= dst can be eliminated because from the check for dst[i+1] we know: +// 1. dst + n does not wrap. +// 2. dst + i + 1 < dst + n +// +// 1. and 2. together imply dst + i + 1 does not wrap, and dst + i + 2 also +// does not (note the < in 2.). Hence dst + i + 2 >= dst is true. +// +// FIXME: Regressed at the moment, rdar://120485098. +// CHECK-LABEL: @loop_accesses_eliminate_second_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP28_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT17:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX10:%.*]] = getelementptr i8, ptr [[TMP3]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[TMP3]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX10]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND20:%.*]] = and i1 [[TMP5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX10]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND21:%.*]] = and i1 [[TMP7]], [[OR_COND20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND21]], label [[CONT17]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_second_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) { + dst[i+1] = 0; + dst[i+2] = 0; + } +} + +// We can eliminate the checks for dst[i+1], because the earlier checks for +// dst[i+2] make them redundant. +// CHECK-LABEL: @loop_accesses_eliminate_later_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT17:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND19:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND19]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX10:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[ARRAYIDX10]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[ARRAYIDX10]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND20:%.*]] = and i1 [[TMP6]], [[TMP7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[ARRAYIDX10]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND21:%.*]] = and i1 [[TMP8]], [[OR_COND20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND21]], label [[CONT17]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 2000 +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_later_checks(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < 2000; i += 1) { + dst[i+2] = 0; + dst[i+1] = 0; + } +} + +// The checks for dst[4], dst[3] and dst[2] should be removed, because they are +// redundant after checking dst[5]. +// CHECK-LABEL: @elim_consecutive_writes( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[DST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[DST]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND55:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND55]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX8:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX8]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND56:%.*]] = and i1 [[TMP6]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX8]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND57:%.*]] = and i1 [[TMP7]], [[OR_COND56]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND57]], label [[CONT15:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX8]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT54:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont54: +// CHECK-NEXT: [[ARRAYIDX21:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX34:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX34]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void elim_consecutive_writes(int* __counted_by(n) dst, unsigned n) { + dst[1] = 0; // Need to check for wrap and against upper bound. + dst[5] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. +} + +// CHECK-LABEL: @elim_consecutive_writes2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[IDX:%.*]], 3 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND55:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND55]], label [[CONT2:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX8:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST]], i64 20, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX8]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND56:%.*]] = and i1 [[TMP6]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND56]], label [[CONT15:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX8]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT41:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: [[ARRAYIDX21:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX34:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX34]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[DST]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[TMP7]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP4]], [[TMP7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND62:%.*]] = and i1 [[TMP9]], [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[TMP4]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND63:%.*]] = and i1 [[TMP10]], [[OR_COND62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND63]], label [[CONT54:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont54: +// CHECK-NEXT: store i32 0, ptr [[TMP4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[IF_END]] +// CHECK: if.end: +// CHECK-NEXT: ret void +// +void elim_consecutive_writes2(int* __counted_by(n) dst, unsigned n, unsigned idx) { + if (idx >= 4) { + dst[idx] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. + dst[5] = 0; // Need to check against upper bound. + } +} + +// TODO +// CHECK-LABEL: @count_ptr_relations( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[LAND_RHS:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARG:%.*]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[N]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = add nuw nsw i64 [[IDX_EXT]], 4611686018427387903 +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = and i64 [[TMP0]], 4611686018427387903 +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ult i64 [[SUB_PTR_DIV]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP:%.*]], label [[CONT61:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont61: +// CHECK-NEXT: store i32 0, ptr [[ARG]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX54:%.*]] = getelementptr i32, ptr [[ARG]], i64 [[CONV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX54]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_PTR64:%.*]] = getelementptr inbounds i32, ptr [[BOUND_PTR_ARITH]], i64 [[CONV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARG]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR64]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND93:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND93]], label [[CONT74:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont74: +// CHECK-NEXT: store i32 1, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[SUB80:%.*]] = add i32 [[N]], -2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[IDXPROM81:%.*]] = zext i32 [[SUB80]] to i64 +// CHECK-NEXT: [[ARRAYIDX82:%.*]] = getelementptr i32, ptr [[BOUND_PTR_ARITH]], i64 [[IDXPROM81]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARRAYIDX82]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR64]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX82]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND95:%.*]] = and i1 [[TMP5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX82]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND96:%.*]] = and i1 [[TMP7]], [[OR_COND95]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND96]], label [[CONT89:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont89: +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX82]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: ret void +// +void count_ptr_relations(int *__counted_by(n) arg, unsigned n) { + if (n == 0) + return; + unsigned m = n-1; + int *__counted_by(m) ptr = arg + 1; + arg[0] = 0; + arg[n-1] = 0; + ptr[0] = 1; // eliminate checks? + ptr[m-1] = 1; // eliminate checks? +} + +// CHECK-LABEL: @ptrinc0( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[INLEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i32 [[INLEN]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[WHILE_END:%.*]], label [[WHILE_BODY:%.*]] +// CHECK: while.body: +// CHECK-NEXT: [[INC_09:%.*]] = phi i32 [ [[INC3:%.*]], [[CONT2:%.*]] ], [ 0, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CURRENTBUF_SROA_0_08:%.*]] = phi ptr [ [[TMP0:%.*]], [[CONT2]] ], [ [[BUF]], [[ENTRY]] ] +// CHECK-NEXT: [[TMP0]] = getelementptr i8, ptr [[CURRENTBUF_SROA_0_08]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[CURRENTBUF_SROA_0_08]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i8 0, ptr [[CURRENTBUF_SROA_0_08]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC3]] = add nuw i32 [[INC_09]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC3]], [[INLEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc0(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned inc = 0; + char *currentBuf = buf; + while (inc < inLen) { + *currentBuf = 0; + inc++; + currentBuf++; + } +} + +// TODO rdar://75687730 +// CHECK-LABEL: @ptrinc1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[INLEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP_NOT6:%.*]] = icmp eq i32 [[INLEN]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT6]], label [[WHILE_END:%.*]], label [[WHILE_BODY:%.*]] +// CHECK: while.body: +// CHECK-NEXT: [[CURRENTLEN_08:%.*]] = phi i32 [ [[DEC:%.*]], [[CONT2:%.*]] ], [ [[INLEN]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CURRENTBUF_SROA_0_07:%.*]] = phi ptr [ [[TMP0:%.*]], [[CONT2]] ], [ [[BUF]], [[ENTRY]] ] +// CHECK-NEXT: [[TMP0]] = getelementptr i8, ptr [[CURRENTBUF_SROA_0_07]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[CURRENTBUF_SROA_0_07]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[CURRENTBUF_SROA_0_07]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND3]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i8 0, ptr [[CURRENTBUF_SROA_0_07]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DEC]] = add i32 [[CURRENTLEN_08]], -1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[DEC]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc1(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned currentLen = inLen; + char *currentBuf = buf; + while (currentLen != 0) { + *currentBuf = 0; + currentLen--; + currentBuf++; + } +} + +// TODO: rdar://99414486 - All BoundsSafety checks should be removed. +// CHECK-LABEL: @test_reforge_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult i32 [[IDX:%.*]], [[TABLE_SIZE:%.*]] +// CHECK-NEXT: [[CMP1_NOT39:%.*]] = icmp ne i32 [[IDX]], 0 +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1_NOT39]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[FOR_BODY_PREHEADER:%.*]], label [[RETURN:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[RETURN]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TABLE:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp eq i32 [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label [[IF_THEN5:%.*]], label [[FOR_COND]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: if.then5: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr i32, ptr [[TABLE]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[IDX_EXT_LE:%.*]] = zext i32 [[TABLE_SIZE]] to i64 +// CHECK-NEXT: [[ADD_PTR_LE:%.*]] = getelementptr inbounds i32, ptr [[TABLE]], i64 [[IDX_EXT_LE]] +// CHECK-NEXT: [[DOTNOT33:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH_LE]], [[TABLE]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT33]], label [[TRAP:%.*]], label [[RETURN]], {{!annotation ![0-9]+}} +// CHECK: return: +// CHECK-NEXT: [[RETVAL_SROA_4_3:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[ADD_PTR_LE]], [[IF_THEN5]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_3:%.*]] = phi ptr [ null, [[ENTRY]] ], [ [[BOUND_PTR_ARITH_LE]], [[IF_THEN5]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[RETVAL_SROA_0_3]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[RETVAL_SROA_4_3]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test_reforge_indexable(unsigned idx, int*__counted_by(table_size) table, + unsigned table_size, int value) { + if (idx >= table_size) + return 0; + + for (unsigned i = 0; i < idx; i++) { + int *__single value_entry = &table[i]; + if (*value_entry == value) + return &table[i]; + } + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c new file mode 100644 index 0000000000000..27a15d32bc3a9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - + +#include + +enum Enum {ZERO=0, ONE}; + +void foo(int *__bidi_indexable ptr, enum Enum e) { + *(ptr + e); +} diff --git a/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c new file mode 100644 index 0000000000000..2bdd9531b750b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - + +#include + +typedef unsigned long size_t; + +void foo(int *__bidi_indexable ptr, size_t idx) { + *(ptr + idx); +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c new file mode 100644 index 0000000000000..8eaea9f9b8e58 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c @@ -0,0 +1,133 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-ARM-O2 %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-ARM-O2 %s + +#include +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[UPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[UPTR]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BPTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 -1 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[BPTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP18]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP19]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP20:%.*]] = load i32, ptr [[WIDE_PTR_PTR8]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP20]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3:[0-9]+]] +// CHECK-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 40 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND]], label [[CONT14:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont14: +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3]] +// CHECK-O2-NEXT: ret i32 undef +// +// CHECK-ARM-O2-LABEL: @main( +// CHECK-ARM-O2-NEXT: entry: +// CHECK-ARM-O2-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-ARM-O2-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3:[0-9]+]] +// CHECK-ARM-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i32 40 +// CHECK-ARM-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i32 -4 +// CHECK-ARM-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: br i1 [[OR_COND]], label [[CONT14:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2: trap: +// CHECK-ARM-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-ARM-O2: cont14: +// CHECK-ARM-O2-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3]] +// CHECK-ARM-O2-NEXT: ret i32 undef +// +int main() { + int arr[10]; + int *__indexable uPtr = arr; + int *__bidi_indexable bptr = uPtr; + --bptr; + return *bptr; // trap : underflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c new file mode 100644 index 0000000000000..488f63e26f227 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c @@ -0,0 +1,91 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR:%.*]], i64 4 +// O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 40 +// O2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT11:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont11: +// O2-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP3]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[UPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: [[BPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UPTR]], i64 16, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[UPTR]], i64 16, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 10 +// O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT12:%.*]], label [[TRAP11:%.*]], {{!annotation ![0-9]+}} +// O0: trap11: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont12: +// O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// O0-NEXT: ret i32 [[TMP12]] +// +int foo(int * arr) { + int *__indexable uPtr = arr; + int *__bidi_indexable bptr = uPtr; +bingo: + return uPtr[10]; // trap : overflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c new file mode 100644 index 0000000000000..d97b45185081b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c @@ -0,0 +1,88 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: br label [[BINGO:%.*]] +// CHECK-O0: bingo: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int main() { + int arr[10] = {0}; + int *__indexable p = arr; +bingo:; + return p[9]; // ok +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c new file mode 100644 index 0000000000000..156f7a28bcbc8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[10]; + int *__indexable p = arr; + return p[10]; // trap : overflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c new file mode 100644 index 0000000000000..be2b160b65a53 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c @@ -0,0 +1,32 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +#include + +// O2-LABEL: @TestFail( +// O2-NEXT: entry: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// O2-NEXT: unreachable +// +int TestFail() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; + int *bp = dcp; + return *bp; // run-time trap : oob - dereference to zero-bound pointer +} + + + +// O2-LABEL: @TestOK( +// O2-NEXT: entry: +// O2-NEXT: ret void +// +void TestOK() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; + int *bp = dcp; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c new file mode 100644 index 0000000000000..9d5bac82c13ee --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O2-LABEL: @TestFail( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-O2-NEXT: unreachable +// +int TestFail() { + int array[5] = {4, 3, 2, 1, 0}; + int len = 2; + int *__counted_by(len) dcp = array; + int *bp = dcp; + return bp[2]; // oob - run-time trap +} + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 3 +// +int TestOK() { + int array[5] = {4, 3, 2, 1, 0}; + int len = 2; + int *__counted_by(len) dcp = array; + int *bp = dcp; + return bp[1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c new file mode 100644 index 0000000000000..4e426a17bb7f9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c @@ -0,0 +1,108 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[BP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: store i32 0, ptr [[LEN]], align 4 +// O0-NEXT: store i32 3, ptr [[I]], align 4 +// O0-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: [[TMP6:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP7]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[BP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: [[TMP13:%.*]] = load i32, ptr [[WIDE_PTR_PTR16]], align 4 +// O0-NEXT: ret i32 [[TMP13]] +// +int main() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; +bingo:; + int *bp = dcp; + return *bp; // run-time trap : oob - dereference to zero-bound pointer +} + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c new file mode 100644 index 0000000000000..6135281eb3209 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c @@ -0,0 +1,117 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// XFAIL: * +// FIXME: rdar://80820825 + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +#include + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; +} + +// CHECK-O2-LABEL: @TestOK2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK2() { + int len = 0; + int arr[10]; + int *__single __counted_by(len) dcp = arr + 10; +} + +// CHECK-O2-LABEL: @TestFail1( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail1() { + int len = 0; + int arr[10]; + int *__single __counted_by(len) dcp = arr + 10; + int *__single tp = dcp; // trap +} + +// CHECK-O2-LABEL: @TestFail2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int TestFail2() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return *tp; // trap +} + +// CHECK-O2-LABEL: @TestFail3( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int TestFail3() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return tp[0]; // trap +} + +struct Data { + int *ptr; + char *cp; +}; + +// CHECK-O2-LABEL: @TestFail4( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int *TestFail4() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return tp->ptr; // trap +} + +// CHECK-O2-LABEL: @TestFail5( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +char TestFail5() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return *(tp->cp); // trap +} + +// CHECK-O2-LABEL: @TestFail6( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +char *TestFail6() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return tp->cp; // trap +} + +// CHECK-O2-LABEL: @TestFail7( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail7() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + char **__single cp = &tp->cp; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c new file mode 100644 index 0000000000000..ddbc7f21d8991 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c @@ -0,0 +1,115 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + + +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[LEN_INIT_ADDR:%.*]] = alloca i32, align 4 +// O0-NEXT: [[I:%.*]] = alloca [3 x i32], align 4 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 [[LEN_INIT:%.*]], ptr [[LEN_INIT_ADDR]], align 4 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[I]], ptr align 4 @__const.foo.i, i64 12, i1 false) +// O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_INIT_ADDR]], align 4 +// O0-NEXT: store i32 [[TMP0]], ptr [[LEN]], align 4 +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [3 x i32], ptr [[I]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 3 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: [[TMP6:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP7]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR16]], null, {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT24:%.*]], {{!annotation ![0-9]+}} +// O0: boundscheck.notnull: +// O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT24]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: store ptr [[WIDE_PTR_PTR16]], ptr [[TP]], align 8 +// O0-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TP]], align 8 +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP14]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int len_init) { + int i[3] = {3}; + int len = len_init; + int *__single __counted_by(len) dcp = i; +bingo:; + int *__single tp = dcp; + + return *tp; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c new file mode 100644 index 0000000000000..22badb1f3ac0c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c @@ -0,0 +1,154 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +#include + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[BP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[ARR]], ptr align 16 @__const.main.arr, i64 40, i1 false) +// O0-NEXT: store i32 5, ptr [[LEN]], align 4 +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP3]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP6]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP5]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR16]], null, {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP10]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT24:%.*]], {{!annotation ![0-9]+}} +// O0: boundscheck.notnull: +// O0-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT24]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: store ptr [[WIDE_PTR_PTR16]], ptr [[TP]], align 8 +// O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT25:%.*]] = sext i32 [[TMP14]] to i64 +// O0-NEXT: [[ADD_PTR26:%.*]] = getelementptr inbounds i32, ptr [[TMP13]], i64 [[IDX_EXT25]] +// O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR26]], ptr [[TMP16]], align 8 +// O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP13]], ptr [[TMP17]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[BP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// O0-NEXT: [[TMP18:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR29]], i64 5 +// O0-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// O0-NEXT: [[TMP19:%.*]] = icmp ult ptr [[TMP18]], [[WIDE_PTR_UB31]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP19]], label [[CONT35:%.*]], label [[TRAP34:%.*]], {{!annotation ![0-9]+}} +// O0: trap34: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont35: +// O0-NEXT: [[TMP20:%.*]] = icmp uge ptr [[TMP18]], [[WIDE_PTR_LB33]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP20]], label [[CONT37:%.*]], label [[TRAP36:%.*]], {{!annotation ![0-9]+}} +// O0: trap36: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont37: +// O0-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP18]], align 4 +// O0-NEXT: ret i32 [[TMP21]] +// +// O2-LABEL: @main( +// O2-NEXT: entry: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable +// +int main() { + int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + int len = 5; + int *__counted_by(len) dcp = arr; + int *__single tp = dcp; // ok: check len >= 1 or dcp is null + int *bp = dcp; // construct bounds pointer based on calculated bounds +bingo:; + return bp[5]; // oob trap! +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c new file mode 100644 index 0000000000000..a1bbcbdfc5def --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c @@ -0,0 +1,46 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm -mllvm -enable-constraint-elimination %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -mllvm -enable-constraint-elimination %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// piggy-back test for -fcoverage-mapping +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o /dev/null + +#include + +// The range check can be removed because 'i >= 0 && i < len'. + +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-O2-NEXT: br i1 [[CMP6]], label [[CONT1_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK-O2: for.body.preheader: +// CHECK-O2-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-O2-NEXT: br label [[CONT1:%.*]] +// CHECK-O2: for.cond.cleanup: +// CHECK-O2-NEXT: ret void +// CHECK-O2: for.body: +// CHECK-O2-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[CONT1_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1]] ] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-O2-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-O2-NEXT: store i32 [[TMP1]], ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-O2-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-O2-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[CONT1]], !llvm.loop [[LOOP6:![0-9]+]] +// +void foo(int *__counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] = i; +} + +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 9 +// +int main() { + int arr[10]; + foo(arr, 10); + return arr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c new file mode 100644 index 0000000000000..6e1aaabb9eaf2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, int idx) { + ptr -= idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} + +// CHECK: define{{.*}} i32 @foo({{.*}}) +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK: define{{.*}} i32 @main() #0 { +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c new file mode 100644 index 0000000000000..85314f2b3ddb1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, unsigned idx) { + ptr += idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, 42); +} + +// CHECK-LABEL: @foo +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK-LABEL: @main +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c new file mode 100644 index 0000000000000..3c230cf801d12 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, int idx) { + ptr += idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} + +// CHECK: define{{.*}} i32 @foo({{.*}}) +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK: define{{.*}} i32 @main() #0 { +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c new file mode 100644 index 0000000000000..f7efe5e59c81d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// +#include + +typedef struct { + int *__counted_by(len) buf; + int len; +} S; + +void use(int *, int); + +// CHECK-LABEL: define dso_local range(i32 -1, 1) i32 @access1( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 2 +// CHECK-NEXT: br i1 [[CMP]], label [[CLEANUP:%.*]], label [[CONT11:%.*]] +// CHECK: cont11: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef [[TMP2]]) #[[ATTR3:[0-9]+]] +// CHECK-NEXT: br label [[CLEANUP]] +// CHECK: cleanup: +// CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ 0, [[CONT11]] ], [ -1, [[ENTRY:%.*]] ] +// CHECK-NEXT: ret i32 [[RETVAL_0]] +// +int access1(S *p) { + int *Ptr = p->buf; + int Len = p->len; + + if (Len < 2) return -1; + int v = *Ptr++; + use(Ptr, v); + return 0; +} + +// CHECK-LABEL: define dso_local range(i32 -1, 1) i32 @access2( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 2 +// CHECK-NEXT: br i1 [[CMP]], label [[CLEANUP:%.*]], label [[CONT11:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META10:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META10]] +// CHECK: cont11: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef [[TMP2]]) #[[ATTR3]] +// CHECK-NEXT: [[BOUND_PTR_ARITH14:%.*]] = getelementptr i8, ptr [[TMP1]], i64 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[TMP1]], !annotation [[META12:![0-9]+]] +// CHECK-NEXT: [[OR_COND35:%.*]] = and i1 [[TMP3]], [[TMP4]], !annotation [[META12]] +// CHECK-NEXT: br i1 [[OR_COND35]], label [[CONT22:%.*]], label [[TRAP:%.*]], !annotation [[META11]] +// CHECK: cont22: +// CHECK-NEXT: [[DOTNOT39:%.*]] = icmp eq ptr [[BOUND_PTR_ARITH14]], null, !annotation [[META13:![0-9]+]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH14]], [[ADD_PTR]], !annotation [[META11]] +// CHECK-NEXT: [[OR_COND40:%.*]] = select i1 [[DOTNOT39]], i1 true, i1 [[TMP5]], !annotation [[META11]] +// CHECK-NEXT: br i1 [[OR_COND40]], label [[CONT32:%.*]], label [[TRAP]], !annotation [[META13]] +// CHECK: cont32: +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA9]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH14]], i32 noundef [[TMP6]]) #[[ATTR3]] +// CHECK-NEXT: br label [[CLEANUP]] +// CHECK: cleanup: +// CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ 0, [[CONT32]] ], [ -1, [[ENTRY:%.*]] ] +// CHECK-NEXT: ret i32 [[RETVAL_0]] +// +int access2(S *p) { + int *Ptr = p->buf; + int Len = p->len; + + if (Len < 2) return -1; + int v = *Ptr++; + use(Ptr, v); + v = *Ptr++; + use(Ptr, v); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c new file mode 100644 index 0000000000000..e6f7b0fce22df --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c @@ -0,0 +1,39 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct foo { + int a, b, c; +}; + +// CHECK-LABEL: @sum( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[F:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_F_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_F_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_F_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[F]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_F_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 12 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT19:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[B]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP3]] +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[C]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD20:%.*]] = add nsw i32 [[ADD]], [[TMP5]] +// CHECK-NEXT: ret i32 [[ADD20]] +// +int sum(struct foo *__bidi_indexable f) { + // This should generate only one check at O2. + return f->a + f->b + f->c; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c new file mode 100644 index 0000000000000..fcc5080b70226 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[I:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 10 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: br label [[BINGO:%.*]] +// CHECK-O0: bingo: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[I]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[10]; + + int *i = &arr[10]; +bingo:; + return *i; // run-time trap : oob +} + +// + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c new file mode 100644 index 0000000000000..d90fcc455a394 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c @@ -0,0 +1,16 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int main() { + int arr[10]; + + int *i = &arr[10]; // ok. no dereference. + + return 0; +} + +// CHECK-NOT: @llvm.ubsantrap diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c new file mode 100644 index 0000000000000..dada74df2e93b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c @@ -0,0 +1,73 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR:%.*]], i64 4 +// O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 40 +// O2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP3]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[BIDI_ARR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BIDI_ARR]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP4]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP6:%.*]] = icmp uge ptr [[TMP4]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP6]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP4]], align 4 +// O0-NEXT: store i32 [[TMP7]], ptr [[I]], align 4 +// O0-NEXT: [[TMP8:%.*]] = load i32, ptr [[I]], align 4 +// O0-NEXT: ret i32 [[TMP8]] +// +int foo(int * arr) { + int * bidi_arr = arr; +bingo:; + int i = bidi_arr[10]; + + return i; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c new file mode 100644 index 0000000000000..f17eba2985270 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c @@ -0,0 +1,77 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=O2 %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=O2 %s + +#include + +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// O0-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 5 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[TMP6]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[TMP6]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: store i32 [[TMP0]], ptr [[TMP6]], align 4 +// O0-NEXT: ret void +// +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN:%.*]] to i64 +// O2-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BUF]], i64 20 +// O2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[BUF]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: store i32 [[LEN]], ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret void +// +void foo(int *__counted_by(len) buf, int len) { + buf[5] = len; +} + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c new file mode 100644 index 0000000000000..67bd7863a7c3e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c @@ -0,0 +1,101 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +struct S { + int *p; + long i; +}; + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// O2-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[S]]) #[[ATTR3:[0-9]+]] +// O2-NEXT: store ptr [[A:%.*]], ptr [[S]], align 8, {{!tbaa ![0-9]+}} +// O2-NEXT: [[I1:%.*]] = getelementptr inbounds i8, ptr [[S]], i64 8 +// O2-NEXT: store i64 [[I:%.*]], ptr [[I1]], align 8, {{!tbaa ![0-9]+}} +// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[S]], i64 16 +// O2-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 24 +// O2-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP1]], [[TMP0]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT9:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont9: +// O2-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr nonnull [[S]]) #[[ATTR3]] +// O2-NEXT: ret void +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[I_ADDR:%.*]] = alloca i64, align 8 +// O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// O0-NEXT: [[SP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: store i64 [[I:%.*]], ptr [[I_ADDR]], align 8 +// O0-NEXT: [[P:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: store ptr [[TMP0]], ptr [[P]], align 8 +// O0-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// O0-NEXT: [[TMP1:%.*]] = load i64, ptr [[I_ADDR]], align 8 +// O0-NEXT: store i64 [[TMP1]], ptr [[I1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 1 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP9]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[SP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_S]], ptr [[WIDE_PTR_PTR4]], i64 1 +// O0-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB6]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR4]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT10:%.*]], label [[TRAP9:%.*]], {{!annotation ![0-9]+}} +// O0: trap9: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont10: +// O0-NEXT: [[I11:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[WIDE_PTR_PTR4]], i32 0, i32 1 +// O0-NEXT: store i64 3, ptr [[I11]], align 8 +// O0-NEXT: ret void +// +void foo(int * a, long i) { + struct S s = {a, i}; + struct S *sp = &s.i; // bad cast but we don't check +bingo:; + sp->i = 3; // run-time trap : oob +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c new file mode 100644 index 0000000000000..41ef39421ed41 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c @@ -0,0 +1,94 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name tmp1 + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[ARR:%.*]] = alloca [4 x i32], align 16 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP1TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 16, i1 false) +// O0-NEXT: br label [[TEST_START:%.*]] +// O0: test_start: +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 4 +// O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP1TMP1]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 0 +// O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH2:%.*]] = getelementptr i32, ptr [[TMP13]], i64 -1 +// O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH2]], ptr [[TMP14]], align 8 +// O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 1 +// O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 2 +// O0-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// O0-NEXT: br label [[TEST_END:%.*]] +// O0: test_end: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP22]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// O0: trap3: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont4: +// O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP23]] +// +// O2-LABEL: @main( +// O2-NEXT: entry: +// O2-NEXT: ret i32 0 +// +int main() { + int arr[4] = {0}; +test_start:; + int *p = arr; + p += 4; + p -= 1; +test_end:; + return *p; // ok +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c new file mode 100644 index 0000000000000..939cdc67ab208 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c @@ -0,0 +1,72 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [4 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[4]; + int *p = arr; + p += 4; + return *p; // trap +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c new file mode 100644 index 0000000000000..c2e39e07a194a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[TMP0:%.*]] = load i32, ptr [[A:%.*]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP0]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int * a) { + int *p = a; + int *p_copy = p--; +bingo:; + return *p_copy; // ok +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c new file mode 100644 index 0000000000000..1f2ad1f602a3f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i64 4 +// O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP2:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP2]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int* a) { + int *p = a; +bingo:; + int *p_copy = p--; + return *p; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c new file mode 100644 index 0000000000000..8f5c503af847c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[TMP0:%.*]] = load i32, ptr [[A:%.*]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP0]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int* a) { + int *p = a; + int *p_copy = p++; +bingo:; + return *p_copy; +} + + +// diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c new file mode 100644 index 0000000000000..6fef0b627bf51 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c @@ -0,0 +1,74 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = p++; + + return *p; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c new file mode 100644 index 0000000000000..4e115b4004130 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c @@ -0,0 +1,87 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[A]]) #[[ATTR3:[0-9]+]] +// CHECK-O2-NEXT: store i32 0, ptr [[A]], align 4 +// CHECK-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 4 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// CHECK-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont1: +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[A]]) #[[ATTR3]] +// CHECK-O2-NEXT: ret i32 undef +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = --p; + + return *p; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c new file mode 100644 index 0000000000000..08c778ddfa7df --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i64 4 +// O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP2:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP2]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int * a) { + int *p = a; + int *p_copy = --p; +bingo:; + return *p_copy; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c new file mode 100644 index 0000000000000..6283ef5ad278b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = ++p; + + return *p; // trap +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c new file mode 100644 index 0000000000000..2774ea6a87b29 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c @@ -0,0 +1,74 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = ++p; + + return *p_copy; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c new file mode 100644 index 0000000000000..af0b730eabb9c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 8, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 2 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP_TMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH2:%.*]] = getelementptr i32, ptr [[TMP13]], i64 1 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH2]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP22]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP23]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int main() { + int a[2] = {0}; + int *p = a; + int *p_copy = p++; + ++p; + + return *p_copy; +} diff --git a/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c b/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c new file mode 100644 index 0000000000000..72ee219e5109e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c @@ -0,0 +1,18 @@ + + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +#include + +int f1(char *__bidi_indexable arr, char i) { + return arr[i]; + // CHECK: {{%.*}} = sext i8 {{%.*}} to i64 +} + +int f2(char *__bidi_indexable arr, unsigned char i) { + return arr[i]; + // CHECK: {{%.*}} = zext i8 {{%.*}} to i64 +} diff --git a/clang/test/BoundsSafety/CodeGen/single-null-O2.c b/clang/test/BoundsSafety/CodeGen/single-null-O2.c new file mode 100644 index 0000000000000..977cac8e28a75 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/single-null-O2.c @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int TestOK() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return 0; +} + +// FIXME +// CHECK-O2-LABEL: @TestTrap( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestTrap() { + int len = 1; + char p; + char *__counted_by(len) dcp = &p; + int *__single tp = dcp; // rdar://80816535 +} diff --git a/clang/test/BoundsSafety/CodeGen/single-null.c b/clang/test/BoundsSafety/CodeGen/single-null.c new file mode 100644 index 0000000000000..4ab7a33eb825c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/single-null.c @@ -0,0 +1,64 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: store i32 0, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: store ptr null, ptr [[DCP]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[DCP]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: boundscheck.notnull: +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT4]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont4: +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TP]], align 8 +// CHECK-O0-NEXT: ret i32 0 +// +int main() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c new file mode 100644 index 0000000000000..29ede0f1719e1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c @@ -0,0 +1,209 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__sized_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 2) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 0) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// rdar://118117905 +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[P]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT79:%.*]] = icmp slt i32 [[LEN]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT79]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[I]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[I]], align 4, !tbaa [[TBAA4:![0-9]+]] +// CHECK-NEXT: call void @foo(ptr noundef nonnull [[I]], i32 noundef 2) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr nocapture noundef readonly [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_9_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_9_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_LHS_TRUE:%.*]], !annotation [[META3]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_17_0_P_SROA_IDX]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_17_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[CONT:%.*]], label [[LOR_RHS:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_9_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META13:![0-9]+]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META3]] +// CHECK-NEXT: [[CMP68:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP68]], [[CMP65]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT]], label [[TRAP]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[AGG_TEMP_SROA_0_0_COPYLOAD]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[LEN]], 5 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[TOBOOL_NOT]], [[SPEC_SELECT]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @bar(ptr noundef [[OUT]], ptr noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META15:![0-9]+]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META15]] +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[P]], ptr noundef nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[CMP_NOT90:%.*]] = icmp slt i32 [[TMP1]], 0, !annotation [[META3]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT90]], i1 false, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META16:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META16]] +// CHECK: land.rhs: +// CHECK-NEXT: br i1 [[DOTNOT]], label [[LOR_RHS:%.*]], label [[CONT67:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CMP61:%.*]] = icmp sge i32 [[TMP1]], [[LEN]], !annotation [[META3]] +// CHECK-NEXT: [[CMP64:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP64]], [[CMP61]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT67]], label [[TRAP]], !annotation [[META3]] +// CHECK: cont67: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META3]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +// CHECK: [[META5]] = !{!"int", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"any pointer", [[META6]], i64 0} +// CHECK: [[META13]] = !{!"bounds-safety-generic", [[META14:![0-9]+]]} +// CHECK: [[META14]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META15]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META16]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c new file mode 100644 index 0000000000000..d8e4f4e62ff20 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c @@ -0,0 +1,962 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__sized_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2:![0-9]+]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 0) +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP20:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP19]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP21:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP20]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP9]]) +// CHECK-NEXT: ret void +// +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef -1) +// CHECK-NEXT: ret void +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP15]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP16]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP5]]) +// CHECK-NEXT: ret void +// +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB2:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_PTR]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END36:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB6]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP13:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP13]], label [[LAND_RHS:%.*]], label [[LAND_END36]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER16]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB18]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR20]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB24]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP31]], label [[LAND_RHS33:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs33: +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP34]], [[LAND_RHS33]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP18:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP17]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END36]], !annotation [[META2]] +// CHECK: land.end36: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP18]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef [[TMP0]], i32 noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[OUT]], ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP0]], ptr noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr ptr, ptr [[P]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[COUNT]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4]], label [[TRAP3:%.*]], !annotation [[META6]] +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR7]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL12:%.*]], label [[CONT16:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull12: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_UB9]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT14:%.*]], label [[TRAP13:%.*]], !annotation [[META5]] +// CHECK: trap13: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont14: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_LB11]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT16]], label [[TRAP15:%.*]], !annotation [[META6]] +// CHECK: trap15: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont16: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]], ptr noundef [[WIDE_PTR_PTR7]]) +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL18:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull18: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP14]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_UB28]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END69:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP39:%.*]] = icmp ule ptr [[WIDE_PTR_LB31]], [[WIDE_PTR_PTR34]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS:%.*]], label [[LAND_END69]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR42]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP23]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR53]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB55]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB57]], ptr [[TMP26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB49]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR59]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP64:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP64]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs66: +// CHECK-NEXT: [[CMP67:%.*]] = icmp sle i32 0, [[TMP23]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP67]], [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP28:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP27]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END69]], !annotation [[META2]] +// CHECK: land.end69: +// CHECK-NEXT: [[TMP29:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP28]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP29]], label [[CONT71:%.*]], label [[TRAP70:%.*]], !annotation [[META2]] +// CHECK: trap70: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont71: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP72]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR74:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR73]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB78:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR77]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR74]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP23]], ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP30:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[TMP30]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP32]], label [[BOUNDSCHECK_NOTNULL80:%.*]], label [[BOUNDSCHECK_NULL83:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull80: +// CHECK-NEXT: [[IDX_EXT81:%.*]] = sext i32 [[TMP31]] to i64 +// CHECK-NEXT: [[ADD_PTR82:%.*]] = getelementptr inbounds i8, ptr [[TMP30]], i64 [[IDX_EXT81]] +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR82]], ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP35]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT84:%.*]] +// CHECK: boundscheck.null83: +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP38]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT84]] +// CHECK: boundscheck.cont84: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR86:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR85]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB88:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR87]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR89:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB90:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR89]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR86]] +// +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c b/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c new file mode 100644 index 0000000000000..be4219727287d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c @@ -0,0 +1,129 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --functions "func" "funcInSDK" --include-generated-funcs --version 3 +#include "../AST/SystemHeaders/include/system-header-unsafe-sys.h" + +// RUN: %clang_cc1 -fbounds-safety %s -O0 -triple arm64-apple-iphoneos -emit-llvm -o - | FileCheck %s + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} +// CHECK-LABEL: define dso_local void @funcInSDK +// CHECK-SAME: (ptr noundef [[PTR:%.*]], ptr noundef [[BIDI:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[BIDI]], ptr [[BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 5, [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], !annotation [[META2]] +// CHECK: land.end46: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @funcWithAnnotation(ptr noundef [[TMP0]], ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define dso_local void @func +// CHECK-SAME: (ptr noundef [[PTR:%.*]], ptr noundef [[BIDI:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[BIDI]], ptr [[BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @funcInSDK(ptr noundef [[TMP0]], ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c new file mode 100644 index 0000000000000..14724b9690c5b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c @@ -0,0 +1,70 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void assign(int *__null_terminated p) { + *p = 42; +} + +// CHECK-LABEL: @compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 42 +// CHECK-NEXT: store i32 [[ADD]], ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void compound_assign(int *__null_terminated p) { + *p += 42; +} + +// CHECK-LABEL: @subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void subscript(int *__null_terminated p) { + p[0] = 42; +} + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P:%.*]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void nested_assign(int *__single *__terminated_by(-1) p) { + *p = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c new file mode 100644 index 0000000000000..1177b53bca5cb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c @@ -0,0 +1,71 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @good_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + *p = 42; +} + +// CHECK-LABEL: @good_compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_compound_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + *p += 42; +} + +// CHECK-LABEL: @good_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_subscript(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p[0] = 42; +} + +// CHECK-LABEL: @bad_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + *p = 42; +} + +// CHECK-LABEL: @bad_compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_compound_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + *p += 42; +} + +// CHECK-LABEL: @bad_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_subscript(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + p[0] = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c new file mode 100644 index 0000000000000..3fe3993d83c1e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c @@ -0,0 +1,84 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void assign(int *__null_terminated p) { + *p = 42; +} + +// CHECK-LABEL: @compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP3]], 42 +// CHECK-NEXT: store i32 [[ADD]], ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void compound_assign(int *__null_terminated p) { + *p += 42; +} + +// CHECK-LABEL: @subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret void +// +void subscript(int *__null_terminated p) { + p[0] = 42; +} + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr null, ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +void nested_assign(int *__single *__terminated_by(-1) p) { + *p = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c new file mode 100644 index 0000000000000..e11bd1b475794 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c @@ -0,0 +1,81 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[PTR_COERCE0:%.*]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT2:%.*]] ], [ [[PTR_COERCE0]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds i8, ptr [[TERMINATED_BY_CUR_0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[PTR_COERCE1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT2]], {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TERMINATED_BY_CUR_0]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP1]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__null_terminated indexable(int *__indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @bidi_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp uge ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 [[TMP0]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT8:%.*]] ], [ [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds i8, ptr [[TERMINATED_BY_CUR_0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT8]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TERMINATED_BY_CUR_0]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP1]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__null_terminated bidi_indexable(int *__bidi_indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @nested_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[PTR_COERCE0:%.*]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT2:%.*]] ], [ [[PTR_COERCE0]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds i8, ptr [[TERMINATED_BY_CUR_0]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[PTR_COERCE1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT2]], {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR_0]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP1]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__single *__terminated_by(-1) nested_indexable(int *__single *__indexable ptr) { + return __unsafe_terminated_by_from_indexable(-1, ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c new file mode 100644 index 0000000000000..8d9b02be11a61 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c @@ -0,0 +1,115 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[PTR_COERCE0:%.*]], [[PTR_TO_TERM_COERCE0:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM_COERCE0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT7:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR_TO_TERM_COERCE0]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__null_terminated indexable_indexable(int *__indexable ptr, int *__indexable ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[PTR_TO_TERM:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = or i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], [[DOTNOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[TRAP:%.*]], label [[CONT6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR_TO_TERM]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT9:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__null_terminated bidi_indexable_single(int *__bidi_indexable ptr, int *__single ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[PTR_COERCE0:%.*]], [[PTR_TO_TERM_COERCE0:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM_COERCE0]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT7:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_TO_TERM_COERCE0]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__single *__terminated_by(-1) nested_indexable_indexable(int *__single *__indexable ptr, int *__single *__indexable ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[PTR_TO_TERM:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = or i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], [[DOTNOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[TRAP:%.*]], label [[CONT6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_TO_TERM]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT9:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__single *__terminated_by(-1) nested_bidi_indexable_single(int *__single *__bidi_indexable ptr, int *__single *__single ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c new file mode 100644 index 0000000000000..7e26a633bd46b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c @@ -0,0 +1,66 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +static const struct { + int _dummy; + // We use int32_t instead of int, because we want a type whose size is greater than 1. + int32_t ints[3]; +} foo = { + 0, + {1, 2, 3}, +}; + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr getelementptr inbounds (i8, ptr @foo, i64 4) +// +const int32_t *__terminated_by(3) good(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p + 2); +} + +// CHECK-LABEL: @bad_no_term( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_no_term(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p + 3); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob2(void) { + const int32_t *__indexable p = foo.ints; + const int32_t *__bidi_indexable q = p; + return __unsafe_terminated_by_from_indexable(3, p, q - 1); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob3(void) { + const int32_t *__indexable p = foo.ints; + const int32_t *__bidi_indexable q = (const void *__bidi_indexable)p + sizeof(foo.ints) - 1; + return __unsafe_terminated_by_from_indexable(3, p, q); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c new file mode 100644 index 0000000000000..e19ae065580af --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c @@ -0,0 +1,237 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[PTR_TO_TERM:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE0:%.*]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE1:%.*]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR_TO_TERM]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[WIDE_PTR_PTR3]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__null_terminated indexable_indexable(int *__indexable ptr, int *__indexable ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_TO_TERM_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR_TO_TERM:%.*]], ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i32, ptr [[TMP3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__null_terminated bidi_indexable_single(int *__bidi_indexable ptr, int *__single ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[PTR_TO_TERM:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE0:%.*]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE1:%.*]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR_TO_TERM]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr ptr, ptr [[WIDE_PTR_PTR3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP4]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__single *__terminated_by(-1) nested_indexable_indexable(int *__single *__indexable ptr, int *__single *__indexable ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_TO_TERM_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[PTR_TO_TERM:%.*]], ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr ptr, ptr [[TMP3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP4]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__single *__terminated_by(-1) nested_bidi_indexable_single(int *__single *__bidi_indexable ptr, int *__single *__single ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c new file mode 100644 index 0000000000000..26f5c141fee4b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c @@ -0,0 +1,63 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static const struct { + int _dummy; + int ints[3]; +} foo = { + 0, + {1, 2, 3}, +}; + +// CHECK-LABEL: @good( +// CHECK-NEXT: cont8.2: +// CHECK-NEXT: ret ptr getelementptr inbounds (i8, ptr @foo, i64 4) +// +const int *__terminated_by(3) good(void) { + const int *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p); +} + +// CHECK-LABEL: @good_2( +// CHECK-NEXT: cont8.1: +// CHECK-NEXT: ret ptr getelementptr inbounds (i8, ptr @foo, i64 4) +// +const int *__terminated_by(2) good_2(void) { + const int *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(2, p); +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__null_terminated bad_null(void) { + int *__indexable p = 0; + return __unsafe_null_terminated_from_indexable(p); +} + +// CHECK-LABEL: @bad_no_term( +// CHECK-NEXT: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int *__null_terminated bad_no_term(void) { + const int *__indexable p = foo.ints; + return __unsafe_null_terminated_from_indexable(p); +} + +// CHECK-LABEL: @bad_bidi_lower_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int *__terminated_by(3) bad_bidi_lower_bound(void) { + // Conversion of __bidi_indexable to __indexable should fail. + const int *__bidi_indexable p = foo.ints; + p--; + return __unsafe_terminated_by_from_indexable(3, p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..0d5a80c36a1a9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c @@ -0,0 +1,149 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds i32, ptr [[TERMINATED_BY_CUR1]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TERMINATED_BY_CUR1]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP3]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__null_terminated indexable(int *__indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @bidi_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR8:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds i32, ptr [[TERMINATED_BY_CUR8]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT10:%.*]], label [[TRAP9:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont10: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TERMINATED_BY_CUR8]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__null_terminated bidi_indexable(int *__bidi_indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @nested_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds ptr, ptr [[TERMINATED_BY_CUR1]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR1]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP3]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__single *__terminated_by(-1) nested_indexable(int *__single *__indexable ptr) { + return __unsafe_terminated_by_from_indexable(-1, ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c new file mode 100644 index 0000000000000..e4e4569d1786b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c @@ -0,0 +1,170 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @pre_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int pre_inc(int *__null_terminated p) { + ++p; + return *p; +} + +// CHECK-LABEL: @post_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int post_inc(int *__null_terminated p) { + p++; + return *p; +} + +// CHECK-LABEL: @add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int add_one(int *__null_terminated p) { + p = p + 1; + return *p; +} + +// CHECK-LABEL: @add_one_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int add_one_swap(int *__null_terminated p) { + p = 1 + p; + return *p; +} + +// CHECK-LABEL: @assign_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int assign_add_one(int *__null_terminated p) { + p += 1; + return *p; +} + +// CHECK-LABEL: @sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int sub_minus_one(int *__null_terminated p) { + p = p - (-1); + return *p; +} + +// CHECK-LABEL: @assign_sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int assign_sub_minus_one(int *__null_terminated p) { + p -= -1; + return *p; +} + +// CHECK-LABEL: @add_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_zero(int *__null_terminated p) { + p = p + 0; + return *p; +} + +// CHECK-LABEL: @add_zero_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_zero_swap(int *__null_terminated p) { + p = 0 + p; + return *p; +} + +// CHECK-LABEL: @add_assign_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_assign_zero(int *__null_terminated p) { + p += 0; + return *p; +} + +// CHECK-LABEL: @subscript_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int subscript_zero(int *__null_terminated p) { + return p[0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c new file mode 100644 index 0000000000000..423d4474630cb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c @@ -0,0 +1,39 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + p++; +} + +// CHECK-LABEL: @bad( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + p++; + p++; +} + +// CHECK-LABEL: @bad2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad2(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + *p = 0; + p++; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c new file mode 100644 index 0000000000000..b69a4f7d3b9e3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c @@ -0,0 +1,227 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @pre_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int pre_inc(int *__null_terminated p) { + ++p; + return *p; +} + +// CHECK-LABEL: @post_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int post_inc(int *__null_terminated p) { + p++; + return *p; +} + +// CHECK-LABEL: @add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int add_one(int *__null_terminated p) { + p = p + 1; + return *p; +} + +// CHECK-LABEL: @add_one_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int add_one_swap(int *__null_terminated p) { + p = 1 + p; + return *p; +} + +// CHECK-LABEL: @assign_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int assign_add_one(int *__null_terminated p) { + p += 1; + return *p; +} + +// CHECK-LABEL: @sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int sub_minus_one(int *__null_terminated p) { + p = p - (-1); + return *p; +} + +// CHECK-LABEL: @assign_sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int assign_sub_minus_one(int *__null_terminated p) { + p -= -1; + return *p; +} + +// CHECK-LABEL: @add_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_zero(int *__null_terminated p) { + p = p + 0; + return *p; +} + +// CHECK-LABEL: @add_zero_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_zero_swap(int *__null_terminated p) { + p = 0 + p; + return *p; +} + +// CHECK-LABEL: @add_assign_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_assign_zero(int *__null_terminated p) { + p += 0; + return *p; +} + +// CHECK-LABEL: @subscript_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int subscript_zero(int *__null_terminated p) { + return p[0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c new file mode 100644 index 0000000000000..e52d91d2ddc78 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c @@ -0,0 +1,21 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @unsafe_to_null_terminated( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[P:%.*]] +// +const char *__null_terminated unsafe_to_null_terminated(const char *__unsafe_indexable p) { + return __unsafe_forge_terminated_by(const char *, p, 0); +} + +// CHECK-LABEL: @null_terminated_to_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[P:%.*]] +// +const char *__unsafe_indexable null_terminated_to_unsafe(const char *__null_terminated p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c new file mode 100644 index 0000000000000..a9aace26ba9b6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c @@ -0,0 +1,27 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @unsafe_to_null_terminated( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +const char *__null_terminated unsafe_to_null_terminated(const char *__unsafe_indexable p) { + return __unsafe_forge_terminated_by(const char *, p, 0); +} + +// CHECK-LABEL: @null_terminated_to_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +const char *__unsafe_indexable null_terminated_to_unsafe(const char *__null_terminated p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c new file mode 100644 index 0000000000000..0aaa11d19f0da --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c @@ -0,0 +1,108 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-error=bounds-safety-strict-terminated-by-cast -emit-llvm %s -o - | FileCheck %s + +#include + +// This should be removed by rdar://118390724 +// Until then this is here to make sure that Sema retains a valid AST when emitted as warning, so that CodeGen doesn't crash + +void foo(const char * __null_terminated); +void bar(const char * __null_terminated * __single); + +// CHECK-LABEL: define dso_local void @test +// CHECK-SAME: (ptr noundef [[SP:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[SP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP3:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SPP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[NTPP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTPP2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTPP3:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[SP]], ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[NTP]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[NTP2]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[TMP2]]) +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[TMP3]]) +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[NTP]], align 8 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr ptr, ptr [[SP_ADDR]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[SP_ADDR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[SP_ADDR]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT2]], label [[TRAP1:%.*]] +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]] +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[SPP]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[NTPP]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[NTPP2]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP16]]) +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP17]]) +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[NTPP]], align 8 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: ret void +// +void test(const char * __single sp) { + const char * __null_terminated ntp = sp; + const char * __null_terminated ntp2 = (const char * __null_terminated) sp; + + foo(sp); + foo((const char * __null_terminated) sp); + + const char * __null_terminated ntp3 = ntp; + ntp3 = sp; + ntp3 = (const char * __null_terminated) sp; + + + /* --- Nested --- */ + + const char * __single * __single spp = &sp; + const char * __null_terminated * __single ntpp = spp; + const char * __null_terminated * __single ntpp2 = (const char * __null_terminated * __single) spp; + + bar(spp); + bar((const char * __null_terminated * __single) spp); + + const char * __null_terminated * __single ntpp3 = ntpp; + ntpp3 = spp; + ntpp3 = (const char * __null_terminated * __single) spp; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-term.c b/clang/test/BoundsSafety/CodeGen/terminated-by-term.c new file mode 100644 index 0000000000000..e94b7530bf65e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-term.c @@ -0,0 +1,128 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Check how the terminator and its load instruction are generated for different +// types, values etc. + +// CHECK-LABEL: @nul_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[TMP0]], align 1 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_char(char *__null_terminated p) { + p++; +} + +// CHECK-LABEL: @nul_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_int(int *__null_terminated p) { + p++; +} + +// CHECK-LABEL: @nul_ptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_ptr(int **__null_terminated p) { + p++; +} + +// CHECK-LABEL: @neg_one_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_int(int *__terminated_by(-1) p) { + p++; +} + +// CHECK-LABEL: @neg_one_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_unsigned(unsigned *__terminated_by(-1) p) { + p++; +} + +// CHECK-LABEL: @neg_one_ptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_ptr(int **__terminated_by(-1) p) { + p++; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c new file mode 100644 index 0000000000000..3cf374176a10a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c @@ -0,0 +1,30 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[P:%.*]]) +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[CALL]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[P:%.*]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P]], i64 [[CALL]] +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr i8, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c new file mode 100644 index 0000000000000..c7392ea802b30 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c @@ -0,0 +1,44 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call i64 @strlen(ptr noundef [[TMP0]]) +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[CALL]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP3]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call i64 @strlen(ptr noundef [[TMP0]]) +// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[CALL]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP4]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c new file mode 100644 index 0000000000000..a44dbce11bbe1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c @@ -0,0 +1,74 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -ffreestanding -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -ffreestanding -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Make sure we don't call strlen() when -ffreestanding is set. + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = add i64 [[TMP2]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP3]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP6]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c new file mode 100644 index 0000000000000..72fa3cb919730 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c @@ -0,0 +1,66 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TMP1]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable safe(int *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable unsafe(int *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @nested( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds ptr, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TERMINATED_BY_ELEM]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds ptr, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TMP1]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__single *__indexable nested(int *__single *__terminated_by(-1) p) { + return __terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c new file mode 100644 index 0000000000000..5d06b2fbd1615 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c @@ -0,0 +1,105 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +int *__indexable safe(int *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = add i64 [[TMP2]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP3]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP6]] +// +int *__indexable unsafe(int *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @nested( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load ptr, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TERMINATED_BY_ELEM]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable.0", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +int *__single *__indexable nested(int *__single *__terminated_by(-1) p) { + return __terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c new file mode 100644 index 0000000000000..4bee37e251e4e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static const int ints[__null_terminated 2] = {42, 0}; +static const char *__null_terminated chars = "Hello"; + +// CHECK-LABEL: @good_ints( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @ints, ptr getelementptr inbounds (i8, ptr @ints, i64 4) } +// +const int *__indexable good_ints(void) { + return __terminated_by_to_indexable(ints); +} + +// CHECK-LABEL: @good_ints_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @ints, ptr getelementptr inbounds (i8, ptr @ints, i64 8) } +// +const int *__indexable good_ints_unsafe(void) { + return __unsafe_terminated_by_to_indexable(ints); +} + +// CHECK-LABEL: @good_chars( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @.str, ptr getelementptr inbounds (i8, ptr @.str, i64 5) } +// +const char *__indexable good_chars(void) { + return __terminated_by_to_indexable(chars); +} + +// CHECK-LABEL: @good_chars_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @.str, ptr getelementptr inbounds (i8, ptr @.str, i64 6) } +// +const char *__indexable good_chars_unsafe(void) { + return __unsafe_terminated_by_to_indexable(chars); +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +int *__indexable bad_null(void) { + int *__null_terminated p = 0; + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @bad_null_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +int *__indexable bad_null_unsafe(void) { + int *__null_terminated p = 0; + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c b/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c new file mode 100644 index 0000000000000..be1177d99594d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -ftrap-function=fb_trap -ftrap-function-returns -O2 -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 + +#include + +// X86_64-LABEL: define dso_local i32 @foo( +// X86_64-SAME: ptr noundef readonly [[BAR:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[IDX_EXT:%.*]] = zext i32 [[COUNT]] to i64 +// X86_64-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BAR]], i64 [[IDX_EXT]] +// X86_64-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BAR]], i64 4 +// X86_64-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], !annotation [[META2:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: tail call void @fb_trap(i8 25) #[[ATTR1:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: br label [[CONT]], !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[TMP0]], [[BAR]], !annotation [[META3:![0-9]+]] +// X86_64-NEXT: br i1 [[DOTNOT]], label [[TRAP1:%.*]], label [[CONT2:%.*]], !annotation [[META3]] +// X86_64: trap1: +// X86_64-NEXT: tail call void @fb_trap(i8 25) #[[ATTR1]], !annotation [[META3]] +// X86_64-NEXT: br label [[CONT2]], !annotation [[META3]] +// X86_64: cont2: +// X86_64-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4, !tbaa [[TBAA4:![0-9]+]] +// X86_64-NEXT: ret i32 [[TMP2]] +// +int foo(int *__counted_by(count) bar, unsigned count) { + return bar[1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/trap-function-returns.c b/clang/test/BoundsSafety/CodeGen/trap-function-returns.c new file mode 100644 index 0000000000000..7a72a64cf198a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-function-returns.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -ftrap-function=fb_trap -ftrap-function-returns -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 + +#include + +// X86_64-LABEL: define dso_local i32 @foo +// X86_64-SAME: (ptr noundef [[BAR:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[BAR_ADDR:%.*]] = alloca ptr, align 8 +// X86_64-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: store ptr [[BAR]], ptr [[BAR_ADDR]], align 8 +// X86_64-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// X86_64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BAR_ADDR]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// X86_64-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// X86_64-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 1 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// X86_64-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: call void @fb_trap(i8 25) #[[ATTR1:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: br label [[CONT]], !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], !annotation [[META3:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], !annotation [[META3]] +// X86_64: trap1: +// X86_64-NEXT: call void @fb_trap(i8 25) #[[ATTR1]], !annotation [[META3]] +// X86_64-NEXT: br label [[CONT2]], !annotation [[META3]] +// X86_64: cont2: +// X86_64-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP5]], align 4 +// X86_64-NEXT: ret i32 [[TMP8]] +// +int foo(int *__counted_by(count) bar, unsigned count) { + return bar[1]; +} +//. +// X86_64: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// X86_64: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/trap-optnone.c b/clang/test/BoundsSafety/CodeGen/trap-optnone.c new file mode 100644 index 0000000000000..b18a2267741bd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-optnone.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -O3 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s + +#include + +int consume(char* __bidi_indexable d) __attribute__((optnone)) { + return d[0]; +} + +// CHECK-LABEL: consume +// CHECK: [[ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR:%[-_.[:alnum:]]+]], i32 0, i32 0 +// CHECK: [[PTR:%[-_.[:alnum:]]+]] = load ptr, ptr [[ADDR]], align 8 +// CHECK: [[PTR_INDEXED:%[-_.[:alnum:]]+]] = getelementptr i8, ptr [[PTR]], i64 0 +// CHECK: [[UB_ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR]], i32 0, i32 1 +// CHECK: [[UB:%[-_.[:alnum:]]+]] = load ptr, ptr [[UB_ADDR]], align 8 +// CHECK: [[LB_ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR]], i32 0, i32 2 +// CHECK: [[LB:%[-_.[:alnum:]]+]] = load ptr, ptr [[LB_ADDR]], align 8 +// CHECK: [[CMP1:%[-_.[:alnum:]]+]] = icmp ult ptr [[PTR_INDEXED]], [[UB]] +// CHECK: br i1 [[CMP1]], label %[[CONT1:[-_.[:alnum:]]+]], label %[[TRAP1:[-_.[:alnum:]]+]] + +// CHECK: [[TRAP1]]: +// CHECK: call void @llvm.ubsantrap(i8 25) + +// CHECK: [[CONT1]]: +// CHECK: [[CMP2:%[-_.[:alnum:]]+]] = icmp uge ptr [[PTR_INDEXED]], [[LB]] +// CHECK: br i1 [[CMP2]], label %[[CONT2:[-_.[:alnum:]]+]], label %[[TRAP2:[-_.[:alnum:]]+]] + +// CHECK: [[TRAP2]]: +// CHECK: call void @llvm.ubsantrap(i8 25) + +int main(void) { + char *data = "ab"; + return consume(data); +} + +// CHECK-LABEL: main +// CHECK-NOT: ubsantrap + diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c new file mode 100644 index 0000000000000..1e1a13cde5af3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +int *__indexable convert(int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: @convert( + +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Converted __indexable pointer is below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 7, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c new file mode 100644 index 0000000000000..8f876d27be9cf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll + +#include + +void consume(int *__counted_by(size) buf, int size); + +void use(int *__indexable buf) { + // FIXME(dliew): We should not be emitting a generic trap here. Instead + // we should be emitting much more specific trap reasons. + // rdar://100346924 + consume(buf, 2); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-generic"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: define void @use +// Note: Multiple branches use the `bounds-safety-generic` remark. They are +// different checks but they use the same opt-remark (rdar://100346924). Until +// that's fixed we need to explicitly match against them all so that we +// correctly determine the trap label. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9.]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c new file mode 100644 index 0000000000000..a97d19defa62e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +int* shift(int* __indexable i, int offset) { + i += offset; + return i; +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$New lower bound less than old lower bound", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @shift +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c new file mode 100644 index 0000000000000..04594d62a70a5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O3 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes CHECK,CHECK-FAKE-FRAME %s + +int bad_read(int index) { + int array[] = {0, 1, 2}; + int array2[] = {1, 5, 8}; + // Placing these two accesses on different lines prevents their debug + // location from being being merged to have the same line. So they will + // end up as 0-line locations. + return array[index] + + array2[index]; +} + +// Merging of traps isn't necessarily ideal because it causes debug info to be lost +// and therefore the trap information stored there to be lost too. +// We should investigate preventing merging of traps (rdar://85946510). +// For now just check that when traps get merged we drop the -fbounds-safety trap reason +// to avoid confusing users. + +// Check that only one trap is emitted when optimized. +// CHECK--LABEL: @bad_read +// CHECK: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] +// CHECK-NOT: call void @llvm.ubsantrap(i8 25) + +// Check the merged trap as a 0-line location and scoped within the correct function +// !26 = !DILocation(line: 0, scope: !14) +// !14 = distinct !DISubprogram(name: "bad_read", scope: !15, file: +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]] +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "bad_read" + +// Make sure fake -fbounds-safety traps are not emitted. +// CHECK-FAKE-FRAME-NOT: {{![0-9]+}} = distinct !DISubprogram(name: "Bounds check failed: diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c new file mode 100644 index 0000000000000..9c65907d12c24 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c @@ -0,0 +1,40 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck %s --input-file=%t_bidi.repeated.ll + +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_idx.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_idx.ll %t.sep %t_idx.ll > %t_idx.repeated.ll +// RUN: FileCheck %s --input-file=%t_idx.repeated.ll + +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +typedef struct Data { + int a; + int b; +} Data_t; + +int* TYPE operation(Data_t* TYPE d) { + return &d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer to struct above bounds while taking address of struct member", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 24, column: 12, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c new file mode 100644 index 0000000000000..af2be0ad09712 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +int* __single operation(int* TYPE i) { + return i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer above bounds while casting", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 7, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c new file mode 100644 index 0000000000000..9088cb3a42c54 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +int operation(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c new file mode 100644 index 0000000000000..836758b2ee021 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c @@ -0,0 +1,29 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +typedef struct Data { + int a; + int b; +} Data_t; + +int operation(Data_t* TYPE d) { + return d->a; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ule ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 16, column: 15, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c new file mode 100644 index 0000000000000..f296f55a5aa34 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +int operation(int* TYPE i) { + return *i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 11, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c new file mode 100644 index 0000000000000..112ebce94a3f3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer above bounds while converting __indexable to __terminated_by", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] +// CHECK: [[SRC_LOC]] = !DILocation(line: 9, column: 45, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c new file mode 100644 index 0000000000000..f8477249bda4e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck -v %s --input-file=%t_bidi.repeated.ll +#include + +// Don't need to test `__indexable` because there is no lower bound check. + +typedef struct Data { + int a; + int b; +} Data_t; + +int* __bidi_indexable operation(Data_t* __bidi_indexable d) { + return &d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer to struct below bounds while taking address of struct member", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 16, column: 12, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c new file mode 100644 index 0000000000000..9dbbc21406143 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +// Don't need to test `__indexable` src because there is no lower bound check. + +int* __single operation(int* __bidi_indexable i) { + return i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer below bounds while casting", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 8, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c new file mode 100644 index 0000000000000..5659a9ed17bef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +int operation(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c new file mode 100644 index 0000000000000..ddddcfe12f200 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck %s --input-file=%t_bidi.repeated.ll +#include + +typedef struct Data { + int a; + int b; +} Data_t; + +// Don't need to test `__indexable` because there is no lower bound check + +int operation(Data_t* __bidi_indexable d) { + return d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 16, column: 15, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c new file mode 100644 index 0000000000000..99af47a74c0a2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +// Don't need to test `__indexable` because there is no lower bound check. + +int operation(int* __bidi_indexable i) { + return *i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 8, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c new file mode 100644 index 0000000000000..168035cba4b5e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str, /*terminator=*/ &str[1]); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file + +// CHECK-DAG: [[OPT_REMARK_0:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-ptr-le-term-ptr"} +// CHECK-DAG: [[TRAP_SCOPE_0:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer below bounds", scope: [[FILE_SCOPE_0:![0-9]+]], file: [[FILE_SCOPE_0]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_0:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_0]], inlinedAt: [[SRC_LOC_0:![0-9]+]]) + +// CHECK-DAG: [[OPT_REMARK_1:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term-ptr-no-overflow"} +// CHECK-DAG: [[TRAP_SCOPE_1:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer overflows address space", scope: [[FILE_SCOPE_1:![0-9]+]], file: [[FILE_SCOPE_1]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_1:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_1]], inlinedAt: [[SRC_LOC_1:![0-9]+]]) + +// CHECK-DAG: [[OPT_REMARK_2:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term-ptr-plus-one-le-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE_2:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer above bounds", scope: [[FILE_SCOPE_2:![0-9]+]], file: [[FILE_SCOPE_2]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_2:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_2]], inlinedAt: [[SRC_LOC_2:![0-9]+]]) + +// CHECK-LABEL: ; __SEPERATOR__ +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_0:[a-z0-9]+]], !dbg [[SRC_LOC_0]], !annotation [[OPT_REMARK_0]] +// CHECK: [[TRAP_LABEL_0]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_0]] + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_1:[a-z0-9]+]], !dbg [[SRC_LOC_1]], !annotation [[OPT_REMARK_1]] +// CHECK: [[TRAP_LABEL_1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_1]] + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_2:[a-z0-9]+]], !dbg [[SRC_LOC_2]], !annotation [[OPT_REMARK_2]] +// CHECK: [[TRAP_LABEL_2]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_2]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c new file mode 100644 index 0000000000000..aeabbc13fede1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str, /*terminator=*/ &str[1]); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Cannot find the terminator when converting to __terminated_by pointer from an __indexable pointer", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c new file mode 100644 index 0000000000000..d1731cc8a35c0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +void iterate(const char* __null_terminated str) { + while (*str != '\0') { + ++str; + } +} + +// CHECK-LABEL: @iterate + +// CHECK: %[[CMP_REG:[a-z0-9]+]] = icmp ne i8 %{{.+}}, 0, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 %[[CMP_REG]], label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] + +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Arithmetic on __terminated_by pointer one-past-the-end of the terminator", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c new file mode 100644 index 0000000000000..68d4af17ae27f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +void assign(char* __null_terminated str) { + str[0] = 'x'; +} + +// CHECK-LABEL: @assign + +// CHECK: %[[CMP_REG:[a-z0-9]+]] = icmp ne i8 %{{.+}}, 0, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 %[[CMP_REG]], label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] + +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$The terminator cannot be assigned", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c new file mode 100644 index 0000000000000..58315b1ebb153 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c @@ -0,0 +1,28 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s + +struct foo { + unsigned bar; +}; + +struct foo arr[10]; + +int v; + +int main() { + struct foo *ptr = &arr[v + 1]; +// RECOVER: call void @__ubsan_handle_out_of_bounds + +// TRAP: br {{.*}} label %[[LABEL_TRAP:[a-z0-9]+]], !nosanitize +// TRAP: [[LABEL_TRAP]]: +// TRAP-NEXT: call void @llvm.ubsantrap(i8 {{.*}}) #{{.*}}, !nosanitize + +} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c new file mode 100644 index 0000000000000..04c5c592a76aa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s + +int arr[10]; + +int v; + +int main() { + int *ptr = &arr[v]; +// RECOVER: call void @__ubsan_handle_out_of_bounds + +// TRAP: br {{.*}} label %[[LABEL_TRAP:[a-z0-9]+]], !nosanitize +// TRAP: [[LABEL_TRAP]]: +// TRAP-NEXT: call void @llvm.ubsantrap(i8 {{.*}}) #{{.*}}, !nosanitize + +} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c new file mode 100644 index 0000000000000..06589e5cc2ebc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c @@ -0,0 +1,39 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=nullability-arg,nullability-assign,nullability-return -fsanitize-trap=nullability-arg,nullability-assign,nullability-return -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=nullability-arg,nullability-assign,nullability-return -fsanitize-trap=nullability-arg,nullability-assign,nullability-return -emit-llvm %s -o - | FileCheck %s + +#include + +int* bidi_to_bidi(int * _Nonnull __bidi_indexable ptr, int * __bidi_indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @bidi_to_bidi +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + +int* raw_to_idx(int * _Nonnull __indexable ptr, int * ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @raw_to_idx +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + +int* bidi_to_idx(int * _Nonnull __indexable ptr, int * __bidi_indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @bidi_to_idx +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + + +int* idx_to_bidi(int * _Nonnull __bidi_indexable ptr, int * __indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @idx_to_bidi +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c new file mode 100644 index 0000000000000..a1d8981b44baa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c @@ -0,0 +1,13 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include + +int local_vars() { + int * __bidi_indexable ptr = 0; + return ptr += 1; +} + +// CHECK: call void @llvm.ubsantrap(i8 19) diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c new file mode 100644 index 0000000000000..13a6dc33cfb49 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c @@ -0,0 +1,14 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include + +int fixed_len_array(int * __bidi_indexable ptr, int k) { + return ptr += k; +} + +// CHECK: call void @llvm.ubsantrap(i8 19) diff --git a/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c new file mode 100644 index 0000000000000..6fc4168b85c36 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c @@ -0,0 +1,35 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +#include + +struct pf_addr { + union { + short _addr16[8]; + int _addr32[4]; + } pfa; +}; + +static short foo(struct pf_addr *a, struct pf_addr *b, int *p) { + b->pfa._addr32[0] = a->pfa._addr32[0]; + + *p = 0x10000010; + short ret = b->pfa._addr16[0] + b->pfa._addr16[1]; // 0x1000 + 0x0010 = 4112 + b->pfa._addr16[0] = 3; + return ret; +} + +// CHECK-LABEL: i16 @bar( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret i16 4112 +// +short bar(void) { + struct pf_addr a = {1, 0, 5, 0}; + struct pf_addr b = {3, 0, 7, 0}; + int *p = &b.pfa._addr32[0]; + + return foo(&a, &b, p); +} diff --git a/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c new file mode 100644 index 0000000000000..db351744b983e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c @@ -0,0 +1,25 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -fbounds-safety -Oz -disable-llvm-passes -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -Oz -disable-llvm-passes -triple arm64-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -Oz -disable-llvm-passes -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +#include + +struct pf_addr { + union { + short _addr16[8]; + int _addr32[4]; + } pfa; +}; + +void foo(struct pf_addr *up) { + up->pfa._addr32[0] = 5; + *up->pfa._addr32 = 4; +} + +// CHECK: store i32 5, ptr [[TMP1:%.*]], align 4, !tbaa ![[TBAA_9:[0-9]+]] +// CHECK: store i32 4, ptr [[WIDE_PTR_PTR:%.*]], align 4, !tbaa ![[TBAA_10:[0-9]+]] +// CHECK-DAG: ![[TBAA_4:[0-9]+]] = !{!"omnipotent char", !{{[0-9]+}}, i64 0} +// CHECK-DAG: ![[TBAA_9]] = !{![[TBAA_4]], ![[TBAA_4]], i64 0} +// CHECK-DAG: ![[TBAA_10]] = !{![[TBAA_11:[0-9]+]], ![[TBAA_11]], i64 0} +// CHECK-DAG: ![[TBAA_11]] = !{!"int", !{{[0-9]+}}, i64 0} diff --git a/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c new file mode 100644 index 0000000000000..e924c34249b14 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c @@ -0,0 +1,98 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 5 +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefix=OPT0 %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -disable-llvm-passes -o - \ +// RUN: | FileCheck -check-prefix=OPT2 %s + +#include + +int consume(int* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} +// OPT0-LABEL: define i32 @consume( +// OPT0-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// OPT0-NEXT: [[ENTRY:.*:]] +// OPT0-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPT0-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// OPT0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPT0-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// OPT0-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// OPT0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// OPT0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPT0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPT0-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// OPT0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPT0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPT0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPT0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPT0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPT0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPT0-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// OPT0-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// OPT0: [[TRAP]]: +// OPT0-NEXT: call void asm sideeffect "", "n"(i64 0), !annotation [[META2]] +// OPT0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// OPT0-NEXT: unreachable, !annotation [[META2]] +// OPT0: [[CONT]]: +// OPT0-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META3:![0-9]+]] +// OPT0-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META3]] +// OPT0: [[TRAP1]]: +// OPT0-NEXT: call void asm sideeffect "", "n"(i64 1), !annotation [[META3]] +// OPT0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// OPT0-NEXT: unreachable, !annotation [[META3]] +// OPT0: [[CONT2]]: +// OPT0-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// OPT0-NEXT: ret i32 [[TMP3]] +// +// +// OPT2-LABEL: define i32 @consume( +// OPT2-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// OPT2-NEXT: [[ENTRY:.*:]] +// OPT2-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPT2-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// OPT2-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPT2-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// OPT2-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4, !tbaa [[TBAA6:![0-9]+]] +// OPT2-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !tbaa.struct [[TBAA_STRUCT8:![0-9]+]] +// OPT2-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPT2-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPT2-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4, !tbaa [[TBAA6]] +// OPT2-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPT2-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPT2-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPT2-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPT2-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPT2-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPT2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META9:![0-9]+]] +// OPT2-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META9]] +// OPT2: [[TRAP]]: +// OPT2-NEXT: call void asm sideeffect "", "n"(i64 0), !annotation [[META9]] +// OPT2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META9]] +// OPT2-NEXT: unreachable, !annotation [[META9]] +// OPT2: [[CONT]]: +// OPT2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META10:![0-9]+]] +// OPT2-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !annotation [[META10]] +// OPT2: [[TRAP1]]: +// OPT2-NEXT: call void asm sideeffect "", "n"(i64 1), !annotation [[META10]] +// OPT2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META10]] +// OPT2-NEXT: unreachable, !annotation [[META10]] +// OPT2: [[CONT2]]: +// OPT2-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA6]] +// OPT2-NEXT: ret i32 [[TMP3]] +// +//. +// OPT0: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// OPT0: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. +// OPT2: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// OPT2: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0} +// OPT2: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// OPT2: [[META5]] = !{!"Simple C/C++ TBAA"} +// OPT2: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// OPT2: [[META7]] = !{!"int", [[META4]], i64 0} +// OPT2: [[TBAA_STRUCT8]] = !{i64 0, i64 24, [[TBAA2]]} +// OPT2: [[META9]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// OPT2: [[META10]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c new file mode 100644 index 0000000000000..9c4195bfa8e64 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c @@ -0,0 +1,111 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 5 + +// Make sure that with or without `-fsplit-cold-code` that the trap blocks don't +// get outlined into their own function. Outlining trap blocks is unhelpful +// because it makes debugging harder which is the opposite of what +// `-funique-trap` is tries to do. + +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - -fno-split-cold-code \ +// RUN: | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - -fsplit-cold-code \ +// RUN: | FileCheck %s +#include + +__attribute__((always_inline)) int i_want_to_be_inlined( + int* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// Note: The trap counter is global to the module so even in the presence of +// inlining the counter will be unique. +int consume(int* __bidi_indexable ptr, int* __bidi_indexable ptr2, int idx) { + int other = i_want_to_be_inlined(ptr2, idx); + return ptr[idx]; +} +// CHECK-LABEL: define i32 @i_want_to_be_inlined( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]], i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 0) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[ARRAYIDX]], [[AGG_TEMP_SROA_3_0_COPYLOAD]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[DOTNOT]], label %[[TRAP1:.*]], label %[[CONT2:.*]], !annotation [[META7]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 1) #[[ATTR3]], !annotation [[META7]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: ret i32 [[TMP1]] +// +// +// CHECK-LABEL: define i32 @consume( +// CHECK-SAME: ptr nocapture noundef readonly [[PTR:%.*]], ptr nocapture noundef readonly [[PTR2:%.*]], i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR2]], align 8 +// CHECK-NEXT: [[BYVAL_TEMP_SROA_4_0_PTR2_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR2]], i64 8 +// CHECK-NEXT: [[BYVAL_TEMP_SROA_4_0_COPYLOAD:%.*]] = load ptr, ptr [[BYVAL_TEMP_SROA_4_0_PTR2_SROA_IDX]], align 8 +// CHECK-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr i32, ptr [[BYVAL_TEMP_SROA_0_0_COPYLOAD]], i64 [[IDXPROM_I]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX_I]], [[BYVAL_TEMP_SROA_4_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT_I:.*]], label %[[TRAP_I:.*]], !annotation [[META2]] +// CHECK: [[TRAP_I]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 0) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT_I]]: +// CHECK-NEXT: [[BYVAL_TEMP_SROA_5_0_PTR2_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR2]], i64 16 +// CHECK-NEXT: [[BYVAL_TEMP_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[BYVAL_TEMP_SROA_5_0_PTR2_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTNOT_I:%.*]] = icmp ult ptr [[ARRAYIDX_I]], [[BYVAL_TEMP_SROA_5_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[DOTNOT_I]], label %[[TRAP1_I:.*]], label %[[I_WANT_TO_BE_INLINED_EXIT:.*]], !annotation [[META7]] +// CHECK: [[TRAP1_I]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 1) #[[ATTR3]], !annotation [[META7]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[I_WANT_TO_BE_INLINED_EXIT]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 [[IDXPROM_I]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 2) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[ARRAYIDX]], [[AGG_TEMP_SROA_3_0_COPYLOAD]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[DOTNOT]], label %[[TRAP1:.*]], label %[[CONT2:.*]], !annotation [[META7]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: tail call void asm sideeffect "", "n"(i64 3) #[[ATTR3]], !annotation [[META7]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA8]] +// CHECK-NEXT: ret i32 [[TMP2]] +// +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0} +// CHECK: [[META9]] = !{!"int", [[META5]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge.c new file mode 100644 index 0000000000000..fc819225db036 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge.c @@ -0,0 +1,151 @@ +// REQUIRES: system-darwin + +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +typedef struct { + char str[10]; + int i; +} S; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 16 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR4]], i64 16 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_S:%.*]], ptr [[WIDE_PTR_PTR13]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB17]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[WIDE_PTR_PTR13]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-NEXT: ret i32 [[TMP19]] +// +int foo(void) { + int a = 10; + S *ptr = __unsafe_forge_bidi_indexable(S *, a, sizeof(S)); + ptr = __unsafe_forge_bidi_indexable(S *, ptr, sizeof(S)); + return ptr->i; +} + + + + + + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int bar(void) { + int a = 10; + S *__single ptr = __unsafe_forge_single(S *, a); + ptr = __unsafe_forge_single(S *, ptr); + return ptr->i; +} + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne i8 [[TMP3]], 65, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i8, ptr [[TMP5]], align 1 +// CHECK-NEXT: ret i8 [[TMP6]] +// +char baz(void) { + int a = 10; + char *__terminated_by('A') ptr = __unsafe_forge_terminated_by(char *, a, 'A'); + ptr = __unsafe_forge_terminated_by(char *, ptr+1, 'A'); + return *ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c new file mode 100644 index 0000000000000..56524aa66eb3b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c @@ -0,0 +1,15 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -O2 -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +#include + +void f1(void *__sized_by(size) ptr, unsigned size); + +// X86_64-LABEL: @f2( +// X86_64-NEXT: entry: +// X86_64-NEXT: tail call void @f1(ptr noundef nonnull inttoptr (i32 4096 to ptr), i32 noundef 10) #[[ATTR2:[0-9]+]] +// X86_64-NEXT: ret void +// +void f2(void) { + f1(__unsafe_forge_bidi_indexable(void *, 0x1000, 10), 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c new file mode 100644 index 0000000000000..f25461936e924 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c @@ -0,0 +1,164 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +#include + +void f1(void *__sized_by(size) ptr, unsigned size); + +//. +// X86_64: @static_single.p_compile_time_constant_single = internal constant ptr inttoptr (i64 123400004321 to ptr), align 8 +// X86_64: @static_bidi.p_compile_time_constant_bidi = internal constant %"__bounds_safety::wide_ptr.bidi_indexable.1" { ptr inttoptr (i64 12300321 to ptr), ptr inttoptr (i64 12300333 to ptr), ptr inttoptr (i64 12300321 to ptr) }, align 8 +//. +// X86_64-LABEL: define dso_local void @f2 +// X86_64-SAME: () #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// X86_64-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// X86_64-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr inttoptr (i32 4096 to ptr), ptr [[TMP0]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr getelementptr (i8, ptr inttoptr (i32 4096 to ptr), i64 10), ptr [[TMP1]], align 8 +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr inttoptr (i32 4096 to ptr), ptr [[TMP2]], align 8 +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP5]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR14]], ptr [[TMP7]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB16]], ptr [[TMP8]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_LB18]], ptr [[TMP9]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR6]] to i64, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// X86_64-NEXT: [[CMP:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB]], !annotation [[META2]] +// X86_64-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: unreachable, !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// X86_64-NEXT: call void @f1(ptr noundef [[WIDE_PTR_PTR27]], i32 noundef 10) +// X86_64-NEXT: ret void +// +void f2(void) { + f1(__unsafe_forge_bidi_indexable(void *, 0x1000, 10), 10); +} + +// X86_64-LABEL: define dso_local ptr @static_single +// X86_64-SAME: () #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: ret ptr inttoptr (i64 123400004321 to ptr) +// +unsigned int * __single static_single() { + static unsigned int * __single const p_compile_time_constant_single = __unsafe_forge_single(unsigned int*, 123400004321); + return p_compile_time_constant_single; +} +// X86_64-LABEL: define dso_local void @static_bidi +// X86_64-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable.1") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 @static_bidi.p_compile_time_constant_bidi, i64 24, i1 false) +// X86_64-NEXT: ret void +// +unsigned int * __bidi_indexable static_bidi() { + static unsigned int * __bidi_indexable const p_compile_time_constant_bidi = __unsafe_forge_bidi_indexable(unsigned int*, 12300321, 12); + return p_compile_time_constant_bidi; +} + +// X86_64-LABEL: define dso_local ptr @nonstatic_single +// X86_64-SAME: () #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[P:%.*]] = alloca ptr, align 8 +// X86_64-NEXT: store ptr inttoptr (i64 123400004321 to ptr), ptr [[P]], align 8 +// X86_64-NEXT: ret ptr inttoptr (i64 123400004321 to ptr) +// +unsigned int * __single nonstatic_single() { + unsigned int * __single const p = __unsafe_forge_single(unsigned int*, 123400004321); + return p; +} +// X86_64-LABEL: define dso_local void @nonstatic_bidi +// X86_64-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable.1") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr inttoptr (i32 12300321 to ptr), ptr [[TMP0]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr getelementptr (i8, ptr inttoptr (i32 12300321 to ptr), i64 12), ptr [[TMP1]], align 8 +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr inttoptr (i32 12300321 to ptr), ptr [[TMP2]], align 8 +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 0 +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 1 +// X86_64-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 2 +// X86_64-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 [[P]], i64 24, i1 false) +// X86_64-NEXT: ret void +// +unsigned int * __bidi_indexable nonstatic_bidi() { + unsigned int * __bidi_indexable const p = __unsafe_forge_bidi_indexable(unsigned int*, 12300321, 12); + return p; +} +//. +// X86_64: attributes #[[ATTR0]] = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// X86_64: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// X86_64: attributes #[[ATTR2:[0-9]+]] = { cold noreturn nounwind } +// X86_64: attributes #[[ATTR3:[0-9]+]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// X86_64: attributes #[[ATTR4]] = { noreturn nounwind } +//. +// X86_64: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} +// X86_64: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +// X86_64: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c new file mode 100644 index 0000000000000..da1c3c22f9af9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c @@ -0,0 +1,69 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// Showing the results are the same with or without -fbounds-safety +// RUN: %clang_cc1 -O2 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @diff_2d( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_2d(void) { + int n = 10, m = 13; + int vla[n][m]; + return vla[1] - vla[0]; +} + +// CHECK-LABEL: @diff_3d( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_3d(void) { + int n = 10, m = 13; + int vla[n][m][15]; + return vla[1] - vla[0]; +} + +// CHECK-LABEL: @diff_3d_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 15 +// +unsigned long long diff_3d_2(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return vla[0][1] - vla[0][0]; +} + +// CHECK-LABEL: @diff_2d_cast( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_2d_cast(void) { + int n = 10, m = 13; + int vla[n][m]; + return (int*)vla[1] - (int*)vla[0]; +} + +// CHECK-LABEL: @diff_3d_cast( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 195 +// +unsigned long long diff_3d_cast(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return (int*)vla[1] - (int*)vla[0]; +} + +// CHECK-LABEL: @diff_3d_cast_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 15 +// +unsigned long long diff_3d_cast_2(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return (int*)vla[0][1] - (int*)vla[0][0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c new file mode 100644 index 0000000000000..409851df09286 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c @@ -0,0 +1,304 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s + +#include + +// CHECK-O0-LABEL: @diff_2d( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP4]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca i32, i64 [[TMP5]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP6]] +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul i64 1, [[TMP3]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP11]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER2]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER7]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = mul i64 0, [[TMP3]] +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR9]], i64 [[TMP19]] +// CHECK-O0-NEXT: [[UPPER10:%.*]] = getelementptr inbounds i32, ptr [[TMP20]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER10]], ptr [[TMP22]], align 8 +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR4]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR12]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-O0-NEXT: [[TMP24:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP24]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_2d(int m, int n) { + int vla[m][n]; + return vla[1] - vla[0]; +} + +// CHECK-O0-LABEL: @diff_3d( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[L_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[L:%.*]], ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP4]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca [15 x i32], i64 [[TMP5]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds [15 x i32], ptr [[VLA]], i64 [[TMP6]] +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul i64 1, [[TMP3]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr [15 x i32], ptr [[WIDE_PTR_PTR]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[UPPER2:%.*]] = getelementptr inbounds [15 x i32], ptr [[TMP11]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER2]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER7:%.*]] = getelementptr inbounds [15 x i32], ptr [[VLA]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER7]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = mul i64 0, [[TMP3]] +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr [15 x i32], ptr [[WIDE_PTR_PTR9]], i64 [[TMP19]] +// CHECK-O0-NEXT: [[UPPER10:%.*]] = getelementptr inbounds [15 x i32], ptr [[TMP20]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER10]], ptr [[TMP22]], align 8 +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR4]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR12]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 60 +// CHECK-O0-NEXT: [[TMP24:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP24]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_3d(int m, int n, int l) { + int vla[m][n][15]; + return vla[1] - vla[0]; +} + + +// CHECK-O0-LABEL: @diff_3d_2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[L_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR2:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.4", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.4", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[L:%.*]], ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-O0-NEXT: [[TMP6:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP6]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = mul nuw i64 [[TMP7]], [[TMP5]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca i32, i64 [[TMP8]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP5]], ptr [[__VLA_EXPR2]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul nuw i64 [[TMP9]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul i64 0, [[TMP14]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP17:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[TMP17]] +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER3]], ptr [[TMP19]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP21:%.*]] = mul i64 1, [[TMP5]] +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 [[TMP21]] +// CHECK-O0-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[TMP22]], i64 [[TMP5]] +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER6]], ptr [[TMP24]], align 8 +// CHECK-O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP22]], ptr [[TMP25]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP26:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP27:%.*]] = mul nuw i64 [[TMP26]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER12:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP27]] +// CHECK-O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP28]], align 8 +// CHECK-O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER12]], ptr [[TMP29]], align 8 +// CHECK-O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-O0-NEXT: [[TMP31:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[TMP32:%.*]] = mul i64 0, [[TMP31]] +// CHECK-O0-NEXT: [[TMP33:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR14]], i64 [[TMP32]] +// CHECK-O0-NEXT: [[TMP34:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER15:%.*]] = getelementptr inbounds i32, ptr [[TMP33]], i64 [[TMP34]] +// CHECK-O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP33]], ptr [[TMP35]], align 8 +// CHECK-O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER15]], ptr [[TMP36]], align 8 +// CHECK-O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP33]], ptr [[TMP37]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-O0-NEXT: [[TMP38:%.*]] = mul i64 0, [[TMP5]] +// CHECK-O0-NEXT: [[TMP39:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR17]], i64 [[TMP38]] +// CHECK-O0-NEXT: [[UPPER18:%.*]] = getelementptr inbounds i32, ptr [[TMP39]], i64 [[TMP5]] +// CHECK-O0-NEXT: [[TMP40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP39]], ptr [[TMP40]], align 8 +// CHECK-O0-NEXT: [[TMP41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER18]], ptr [[TMP41]], align 8 +// CHECK-O0-NEXT: [[TMP42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP39]], ptr [[TMP42]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR8]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-O0-NEXT: [[TMP43:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP43]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_3d_2(int m, int n, int l) { + int vla[m][n][l]; + return vla[0][1] - vla[0][0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c b/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c new file mode 100644 index 0000000000000..cfa4abb56a49f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c @@ -0,0 +1,102 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -fsanitize=alignment -fsanitize-trap=alignment -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -fsanitize=alignment -fsanitize-trap=alignment -triple arm64 %s -o - | FileCheck %s + +#include + +struct bstr { + const unsigned char *__counted_by(length) data; + unsigned long length; +}; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[SN:%.*]], ptr [[SN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[SN_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3:[0-9]+]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LENGTH:%.*]] = getelementptr inbounds [[STRUCT_BSTR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = ptrtoint ptr [[LENGTH]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP7:%.*]] = load i64, ptr [[LENGTH]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = ptrtoint ptr [[TMP0]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[TMP9]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont4: +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds [[STRUCT_BSTR]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = ptrtoint ptr [[DATA]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = and i64 [[TMP11]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = icmp eq i64 [[TMP12]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP7]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP14]], i64 [[TMP7]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[TMP18]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT8:%.*]], label [[TRAP7:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap7: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont8: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[TMP18]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[CONT10:%.*]], label [[TRAP9:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont10: +// CHECK-NEXT: [[TMP21:%.*]] = load i8, ptr [[TMP18]], align 1 +// CHECK-NEXT: [[CONV:%.*]] = zext i8 [[TMP21]] to i32 +// CHECK-NEXT: [[CMP11:%.*]] = icmp eq i32 [[CONV]], 170 +// CHECK-NEXT: br i1 [[CMP11]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP22:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP22]] +// +int test(struct bstr *sn) +{ + if (sn->data[2] == 0xAA) + return 0; + return 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c new file mode 100644 index 0000000000000..81f4c26e6f02f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c @@ -0,0 +1,137 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @wide_array_subscript_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_ok() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + buf[4] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_trap1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_array_subscript_trap1() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + buf[5] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_trap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [5 x %"__bounds_safety::wide_ptr.indexable"], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 80, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(80) [[ARR]], i8 0, i64 80, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 80 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 -16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 80, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void wide_array_subscript_trap2() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + buf[-1] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_read_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_read_ok() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)buf[4]; +} + +// CHECK-LABEL: @wide_array_subscript_read_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_read_ok2() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)&buf[5]; +} + +// CHECK-LABEL: @wide_array_subscript_read_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_array_subscript_read_trap() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)buf[5]; +} + +// CHECK-LABEL: @wide_deref_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_deref_ok() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + *(buf + 4) = 0; +} + +// CHECK-LABEL: @wide_deref_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_deref_trap() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + *(buf + 5) = 0; +} + +struct wide_member_t { + int *__bidi_indexable ptr; +}; +// CHECK-LABEL: @wide_member_assign_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_member_assign_ok() { + struct wide_member_t w; + struct wide_member_t *wp = &w; + wp->ptr = 0; +} + +// CHECK-LABEL: @wide_member_assign_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[W:%.*]] = alloca [[STRUCT_WIDE_MEMBER_T:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[W]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[W]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[W]], i64 24 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[W]], i64 48 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP1]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[W]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void wide_member_assign_trap() { + struct wide_member_t w; + struct wide_member_t *wp = &w + 1; + wp->ptr = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c new file mode 100644 index 0000000000000..029133797e52e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c @@ -0,0 +1,235 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @wide_array_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP4]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[TMP4]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[TMP4]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void wide_array_subscript(int *__indexable *__indexable buf, int i) { + buf[i] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_load( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP4]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[TMP4]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: ret void +// +void wide_array_subscript_load(int *__indexable *__indexable buf, int i) { + (void)buf[i]; +} + +// CHECK-LABEL: @wide_array_subscript_addr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: ret void +// +void wide_array_subscript_addr(int *__indexable *__indexable buf, int i) { + (void)&buf[i]; +} + +// CHECK-LABEL: @wide_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[WIDE_PTR_PTR3]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void wide_deref(int *__indexable *__indexable buf, int i) { + *(buf + i) = 0; +} + +struct wide_member_t { + int *__bidi_indexable ptr; +}; +// CHECK-LABEL: @wide_member_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[W_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[W:%.*]], ptr [[W_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[W]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_WIDE_MEMBER_T:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_WIDE_MEMBER_T]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +void wide_member_assign(struct wide_member_t *__bidi_indexable w) { + w->ptr = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c new file mode 100644 index 0000000000000..60323724fb1de --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c @@ -0,0 +1,104 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int foo() { + int arr[10]; + int *ptr = arr; + int *ptr2 = &*ptr; + return ptr2[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar() { + int arr[10]; + int *ptr = arr; + int *ptr2 = &*ptr; + return ptr2[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c new file mode 100644 index 0000000000000..d9bd2f6e4cf97 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c @@ -0,0 +1,159 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +// +// We could have skipped '*&' entirely but this is fine too. +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i64 1 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[WIDE_PTR_PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap10: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont11: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap12: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont13: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int foo() { + int arr[10]; + int *__bidi_indexable ptr = arr; + int *ptr2 = *&ptr; + return ptr2[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i64 1 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[WIDE_PTR_PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap10: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont11: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap12: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont13: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar() { + int arr[10]; + int *__bidi_indexable ptr = arr; + int *ptr2 = *&ptr; + return ptr2[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c new file mode 100644 index 0000000000000..cab0fa61ec2a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c @@ -0,0 +1,26 @@ + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +extern float foo[]; +// CHECK: @foo = external global [0 x float] + +float *__indexable wide_f[] = {foo}; +// CHECK: @wide_f = global [1 x %"__bounds_safety::wide_ptr.indexable"] [%"__bounds_safety::wide_ptr.indexable" { ptr @foo, ptr @foo }] + +extern float bar[]; +float bar[] = {1, 2, 3, 4}; +// CHECK: @bar = global [4 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00] + +float *__indexable wide_f2[] = {bar}; +// CHECK: @wide_f2 = global [1 x %"__bounds_safety::wide_ptr.indexable"] [%"__bounds_safety::wide_ptr.indexable" { ptr @bar, ptr inttoptr (i64 add (i64 ptrtoint (ptr @bar to i64), i64 16) to ptr) }] + +float *__bidi_indexable wide_f3[] = {foo, bar}; +// CHECK: @wide_f3 = global [2 x %"__bounds_safety::wide_ptr.bidi_indexable"] [%"__bounds_safety::wide_ptr.bidi_indexable" { ptr @foo, ptr @foo, ptr @foo }, %"__bounds_safety::wide_ptr.bidi_indexable" { ptr @bar, ptr inttoptr (i64 add (i64 ptrtoint (ptr @bar to i64), i64 16) to ptr), ptr @bar }] + +// CHECK-NOT: @foo +// CHECK-NOT: @wide_f +// CHECK-NOT: @bar +// CHECK-NOT: @wide_f2 +// CHECK-NOT: @wide_f3 diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c new file mode 100644 index 0000000000000..8b7a4a3388d79 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c @@ -0,0 +1,59 @@ + +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK: %"__bounds_safety::wide_ptr.indexable" = type { ptr, ptr } +// CHECK: %"__bounds_safety::wide_ptr.bidi_indexable" = type { ptr, ptr, ptr } + +// CHECK: @i_null = global %"__bounds_safety::wide_ptr.indexable{{.*}}" zeroinitializer, align 8 +// CHECK: @bi_null = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" zeroinitializer, align 8 +int *__indexable i_null = 0; +int *__bidi_indexable bi_null = 0; + +// CHECK: @i_null_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" zeroinitializer, align 8 +// CHECK: @bi_null_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" zeroinitializer, align 8 +void *__indexable i_null_cast = (int *)0; +void *__bidi_indexable bi_null_cast = (int *)0; + +// CHECK: @array = global [10 x i32] zeroinitializer, align 4 +int array[10]; + +// CHECK: @i_array = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr) }, align 8 +// CHECK: @bi_array = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr), ptr @array }, align 8 +int *__indexable i_array = array; +int *__bidi_indexable bi_array = array; + +// CHECK: @i_array_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr) }, align 8 +// CHECK: @bi_array_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr), ptr @array }, align 8 +void *__indexable i_array_cast = array; +void *__bidi_indexable bi_array_cast = array; + +// CHECK: @x = global i32 0, align 4 +int x; + +// CHECK: @i_addrof = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr) }, align 8 +// CHECK: @bi_addrof = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr), ptr @x }, align 8 +int *__indexable i_addrof = &x; +int *__bidi_indexable bi_addrof = &x; + +// CHECK: @i_addrof_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr) }, align 8 +// CHECK: @bi_addrof_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr), ptr @x }, align 8 +void *__indexable i_addrof_cast = &x; +void *__bidi_indexable bi_addrof_cast = &x; + +struct foo { + int x[100]; +}; + +// CHECK: @f = global %struct.foo zeroinitializer, align 4 +struct foo f = {}; + +struct bar { + const void *__bidi_indexable p; +}; + +// CHECK: @b = global %struct.bar { %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @f, ptr inttoptr (i64 add (i64 ptrtoint (ptr @f to i64), i64 400) to ptr), ptr @f } }, align 8 +struct bar b = { + .p = &f +}; diff --git a/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c b/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c new file mode 100644 index 0000000000000..81afd81d7c3b9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c @@ -0,0 +1,50 @@ +// XFAIL: * + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +void Foo(int *__counted_by(len) buf, int len) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + + +int Test() { + int arr[10]; + Foo(arr, trap_if_bigger_than_max(get_len(arr))); + return 0; +} + +// CHECK-LABEL: @Test +// ... +// CHECK: [[RET_GET_LEN:%.*]] = call i64 @get_len +// CHECK: [[RET_TRAP_IF:%.*]] = call i64 @trap_if_bigger_than_max(i64 [[RET_GET_LEN]]), !annotation [[ANNOT_CONV_TO_COUNT:![0-9]+]] +// ... +// CHECK: [[COND_LE_UB:%.*]] = icmp ule i64 {{%.*}}, {{%.*}}, !annotation [[ANNOT_LE_UB:![0-9]+]] +// CHECK: br i1 [[COND_LE_UB]], label %[[LABEL_CONT:.*]], label %[[LABEL_TRAP:.*]], !annotation [[ANNOT_LE_UB]] + +// CHECK: [[LABEL_TRAP]]: +// CHECK: call void @llvm.ubsantrap{{.*}}, !annotation [[ANNOT_LE_UB]] +// CHECK: unreachable + +// 'RET_TRAP_IF' - the result of call 'trap_if_bigger_than_max' is used in count check here and later as argument for function call to @Foo, +// instead of being re-evaluated. +// CHECK: [[LABEL_CONT]]: +// CHECK: [[COND_CONV_TO_COUNT:%.*]] = icmp ule i64 [[RET_TRAP_IF]], {{.*}}, !annotation [[ANNOT_CONV_TO_COUNT]] +// CHECK: br i1 [[COND_CONV_TO_COUNT]], label %[[LABEL_CONT12:.*]], label %[[LABEL_TRAP11:.*]], !annotation [[ANNOT_CONV_TO_COUNT]] + +// CHECK: [[LABEL_TRAP11]]: ; preds = %[[LABEL_CONT]] +// CHECK: call void @llvm.ubsantrap{{.*}}, !annotation [[ANNOT_CONV_TO_COUNT]] +// CHECK: unreachable + +// CHECK: [[LABEL_CONT12]]: ; preds = %[[LABEL_CONT]] +// CHECK: [[ARG_CONV:%.*]] = trunc i64 [[RET_TRAP_IF]] to i32 +// CHECK: call void @Foo(i32* {{%.*}}, i32 [[ARG_CONV]]) +// CHECK: ret i32 0 + +// CHECK: [[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-check-conversion-to-count"} +// CHECK: [[ANNOT_LE_UB]] = !{!"bounds-safety-check-ptr-le-upper-bound"} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c b/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c new file mode 100644 index 0000000000000..b730c11bf6d99 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c @@ -0,0 +1,27 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct foo { + int *__counted_by(count) ptr; + int count; + int not_initialized; +}; + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: ret i32 [[TMP0]] +// +int bar(void) { + struct foo f; + return f.count; +} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c b/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c new file mode 100644 index 0000000000000..79a16095f7ac0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c @@ -0,0 +1,24 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct foo { + int *__null_terminated ptr; + int not_initialized; +}; + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR1:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR1]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__null_terminated bar(void) { + struct foo f; + return f.ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init.c b/clang/test/BoundsSafety/CodeGen/zero-init.c new file mode 100644 index 0000000000000..fc2c3555347f8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +int main() { + int *ptr; + if (!ptr) + return 0; + return 1; +} + +// CHECK: ret i32 0 +// CHECK-NOT ret i32 1 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c b/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c new file mode 100644 index 0000000000000..1e6346df4c464 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c @@ -0,0 +1,1297 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - -O2 | FileCheck %s --check-prefix CHECK-O2 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - -O2 | FileCheck %s --check-prefix CHECK-O2 + +#include +typedef __SIZE_TYPE__ size_t; + + +void counted_by(int *__counted_by(len) p, size_t len); +// CHECK-LABEL: @test_counted_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP76:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP44]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: call void @counted_by(ptr noundef [[WIDE_PTR_PTR47]], i64 noundef 0) +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP12]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB55]], ptr [[TMP20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR56:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR57:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR58:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB59:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR60:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB61:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR60]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP62]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR64:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB66:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR57]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST70:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR64]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB71:%.*]] = sub i64 [[SUB_PTR_LHS_CAST69]], [[SUB_PTR_RHS_CAST70]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV72:%.*]] = sdiv exact i64 [[SUB_PTR_SUB71]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP73:%.*]] = icmp ule i64 0, [[SUB_PTR_DIV72]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP73]], label [[CONT75:%.*]], label [[TRAP74:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap74: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont75: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP76]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR78:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB80:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR79]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR81:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB82:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR81]], align 8 +// CHECK-NEXT: call void @counted_by(ptr noundef [[WIDE_PTR_PTR78]], i64 noundef 0) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_counted_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4:[0-9]+]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @counted_by(ptr noundef nonnull [[ZERO]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @counted_by(ptr noundef nonnull [[TMP0]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_counted_by(void) { + int zero[0]; + int one; + counted_by(zero, 0); + counted_by(&one + 1, 0); +} + +void sized_by(void *__sized_by(len) p, size_t len); +// CHECK-LABEL: @test_sized_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP81:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP98:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP117:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp ule i64 0, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP65]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR67:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR68:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB70:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB72:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR71]], align 8 +// CHECK-NEXT: call void @sized_by(ptr noundef [[WIDE_PTR_PTR68]], i64 noundef 0) +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP21]], i64 1 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP24]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR76]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB78]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB80]], ptr [[TMP31]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB84]], ptr [[TMP32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR86:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB88:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR89:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB90:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR89]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR86]], ptr [[TMP33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB88]], ptr [[TMP34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB90]], ptr [[TMP35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR91:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR92:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR91]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR93:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB94:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR93]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB96:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR95]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP98]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR99:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR100:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR99]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR101:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB102:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR101]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR103:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB104:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR103]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR100]], ptr [[TMP36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB102]], ptr [[TMP37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB104]], ptr [[TMP38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR105:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR106:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR105]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR107:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB108:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR107]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR109:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB110:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR109]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST111:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR92]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST112:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR106]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB113:%.*]] = sub i64 [[SUB_PTR_LHS_CAST111]], [[SUB_PTR_RHS_CAST112]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP114:%.*]] = icmp ule i64 0, [[SUB_PTR_SUB113]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP114]], label [[CONT116:%.*]], label [[TRAP115:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap115: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont116: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP117]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR118:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR119:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR118]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR120:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB121:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR120]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR122:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB123:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR122]], align 8 +// CHECK-NEXT: call void @sized_by(ptr noundef [[WIDE_PTR_PTR119]], i64 noundef 0) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_sized_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @sized_by(ptr noundef nonnull [[ZERO]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @sized_by(ptr noundef nonnull [[TMP0]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_sized_by(void) { + int zero[0]; + int one; + sized_by(zero, 0); + sized_by(&one + 1, 0); +} + +void ended_by(void *__ended_by(end) start, void *end); +// CHECK-LABEL: @test_ended_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP65:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP92:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY4:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY4]], i64 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB11]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB21]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP42:%.*]] = icmp ule ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_PTR37]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP42]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR45]], ptr noundef [[WIDE_PTR_PTR52]]) +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP19]], i64 1 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB62]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB64]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP32]], align 8 +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 0 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[TMP34]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH68:%.*]] = getelementptr i32, ptr [[TMP35]], i64 1 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH68]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 1 +// CHECK-NEXT: [[TMP38:%.*]] = load ptr, ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP38]], ptr [[TMP39]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 2 +// CHECK-NEXT: [[TMP41:%.*]] = load ptr, ptr [[TMP40]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP41]], ptr [[TMP42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB72]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB74]], ptr [[TMP45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP65]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR83:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR84:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR87:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB88:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP89:%.*]] = icmp ule ptr [[WIDE_PTR_PTR77]], [[WIDE_PTR_PTR84]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP89]], label [[CONT91:%.*]], label [[TRAP90:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap90: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont91: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP92]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP99]], ptr align 8 [[AGG_TEMP65]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR100:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR101:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR102:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB103:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR104:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB105:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR104]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR94]], ptr noundef [[WIDE_PTR_PTR101]]) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_ended_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @ended_by(ptr noundef nonnull [[ZERO]], ptr noundef nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @ended_by(ptr noundef nonnull [[BOUND_PTR_ARITH]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_ended_by(void) { + int zero[0]; + int one; + ended_by(zero, zero); + ended_by(&one + 1, &one + 1); +} + +void ended_by_itself(void *__ended_by(end) end); +// CHECK-LABEL: @test_ended_by_itself( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8 +// CHECK-NEXT: call void @ended_by_itself(ptr noundef [[WIDE_PTR_PTR20]]) +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP12]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB32]], ptr [[TMP22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP49:%.*]] = icmp ule ptr [[WIDE_PTR_PTR35]], [[WIDE_PTR_PTR44]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP49]], label [[CONT51:%.*]], label [[TRAP50:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap50: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont51: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @ended_by_itself(ptr noundef [[WIDE_PTR_PTR54]]) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_ended_by_itself( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @ended_by_itself(ptr noundef nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @ended_by_itself(ptr noundef nonnull [[TMP0]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_ended_by_itself(void) { + int zero[0]; + int one; + ended_by_itself(zero); + ended_by_itself(&one + 1); +} + +struct counted_by { + int *__counted_by(len) p; + size_t len; +}; + +// CHECK-LABEL: @test_struct_counted_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_COUNTED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED1:%.*]] = alloca [[STRUCT_COUNTED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[P]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN]], align 8 +// CHECK-NEXT: [[P2:%.*]] = getelementptr inbounds [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP8]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[P2]], align 8 +// CHECK-NEXT: [[LEN10:%.*]] = getelementptr inbounds [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED1]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN10]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_counted_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_counted_by(void) { + int zero[0]; + int one; + (void) (struct counted_by) {zero, 0}; + (void) (struct counted_by) {&one + 1, 0}; +} + +struct sized_by { + void *__sized_by(len) p; + size_t len; +}; + +// CHECK-LABEL: @test_struct_sized_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SIZED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED8:%.*]] = alloca [[STRUCT_SIZED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[P]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN]], align 8 +// CHECK-NEXT: [[P9:%.*]] = getelementptr inbounds [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[P9]], align 8 +// CHECK-NEXT: [[LEN24:%.*]] = getelementptr inbounds [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN24]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_sized_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_sized_by(void) { + int zero[0]; + int one; + (void) (struct sized_by) {zero, 0}; + (void) (struct sized_by) {&one + 1, 0}; +} + +struct ended_by { + void *__ended_by(end) start; + void *end; +}; + +// CHECK-LABEL: @test_struct_ended_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_ENDED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED24:%.*]] = alloca [[STRUCT_ENDED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY10:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER11:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY10]], i64 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY10]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER11]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY10]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[END]], align 8 +// CHECK-NEXT: [[START25:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED24]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP17]], i64 1 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR29]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB31]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB33]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[START25]], align 8 +// CHECK-NEXT: [[END40:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED24]], i32 0, i32 1 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 0 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[TMP32]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH44:%.*]] = getelementptr i32, ptr [[TMP33]], i64 1 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH44]], ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 1 +// CHECK-NEXT: [[TMP36:%.*]] = load ptr, ptr [[TMP35]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 2 +// CHECK-NEXT: [[TMP39:%.*]] = load ptr, ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP39]], ptr [[TMP40]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB48]], ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB50]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR52]], ptr [[END40]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_ended_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_ended_by(void) { + int zero[0]; + int one; + (void) (struct ended_by) {zero, zero}; + (void) (struct ended_by) {&one + 1, &one + 1}; +} + +struct ended_by_itself { + void *__ended_by(end) end; +}; + +// CHECK-LABEL: @test_struct_ended_by_itself( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_ENDED_BY_ITSELF:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED8:%.*]] = alloca [[STRUCT_ENDED_BY_ITSELF]], align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY_ITSELF]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds [[STRUCT_ENDED_BY_ITSELF]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[END9]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_ended_by_itself( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_ended_by_itself(void) { + int zero[0]; + int one; + (void) (struct ended_by_itself) {zero}; + (void) (struct ended_by_itself) {&one + 1}; +} diff --git a/clang/test/BoundsSafety/Driver/asm_is_supported.S b/clang/test/BoundsSafety/Driver/asm_is_supported.S new file mode 100644 index 0000000000000..337a77233eae1 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/asm_is_supported.S @@ -0,0 +1,4 @@ + +// Making sure the Frontend accepts the -fbounds-safety flags in assembly mode - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -fsyntax-only -fbounds-safety %s diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c b/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c new file mode 100644 index 0000000000000..6409bcc8eb23c --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c @@ -0,0 +1,32 @@ + + +// ============================================================================= +// Adoption mode off +// ============================================================================= + +// Check adoption mode is off by default +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=NO-ADOPT-CHECK %s + +// Check adoption mode is off if explicitly requested +// RUN: %clang -fbounds-safety -fno-bounds-safety-adoption-mode -c %s -### \ +// RUN: 2>&1 | FileCheck --check-prefix=NO-ADOPT-CHECK %s + +// NO-ADOPT-CHECK: -fbounds-safety +// NOT-ADOPT-CHECK-NOT: -fbounds-safety-adoption-mode + +// ============================================================================= +// Adoption mode on +// ============================================================================= + +// Check driver passes flag when adoption mode explicitly requested +// RUN: %clang -fbounds-safety -fbounds-safety-adoption-mode -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=ADOPT-CHECK %s + +// Check -fno- followed by -f causes -f to win +// RUN: %clang -fbounds-safety -fno-bounds-safety-adoption-mode \ +// RUN: -fbounds-safety-adoption-mode -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=ADOPT-CHECK %s + +// ADOPT-CHECK: -fbounds-safety +// ADOPT-CHECK: -fbounds-safety-adoption-mode diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c b/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c new file mode 100644 index 0000000000000..725a3f8ac368c --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c @@ -0,0 +1,15 @@ + + +// ALL-NOT: unknown argument + +// RUN: %clang -c %s -### 2>&1 | not grep fexperimental-bounds-safety-attributes + +// RUN: %clang -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | not grep fbounds-safety + +// RUN: %clang -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T0 %s +// T0: -fexperimental-bounds-safety-attributes + +// RUN: %clang -fbounds-safety -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T1 %s +// T1: -fexperimental-bounds-safety-attributes + +// RUN: %clang -fexperimental-bounds-safety-attributes -fno-experimental-bounds-safety-attributes -c %s -### 2>&1 | not grep fexperimental-bounds-safety-attributes diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c b/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c new file mode 100644 index 0000000000000..155c04a87fa93 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c @@ -0,0 +1,88 @@ + + +// RUN: %clang -c %s -### 2>&1 | FileCheck %s --check-prefixes=EMPTY +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | FileCheck %s --check-prefixes=EMPTY + +// EMPTY-NOT: -fbounds-safety-bringup-missing-checks +// EMPTY-NOT: -fno-bounds-safety-bringup-missing-checks +// EMPTY-NOT: -fbounds-safety-bringup-missing-checks=all +// EMPTY-NOT: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=DISABLED +// DISABLED: -fno-bounds-safety-bringup-missing-checks=all +// DISABLED-NOT: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=ENABLED +// ENABLED: -fbounds-safety-bringup-missing-checks=all +// ENABLED-NOT: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_NEG +// POS_NEG: -fbounds-safety-bringup-missing-checks=all +// POS_NEG: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_POS +// NEG_POS: -fno-bounds-safety-bringup-missing-checks=all +// NEG_POS: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ACCESS +// POS_ACCESS: -fbounds-safety-bringup-missing-checks=access_size + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_INDIRECT +// POS_INDIRECT: -fbounds-safety-bringup-missing-checks=indirect_count_update + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_RETURN +// POS_RETURN: -fbounds-safety-bringup-missing-checks=return_size + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ENDED_BY_LOWER +// POS_ENDED_BY_LOWER: -fbounds-safety-bringup-missing-checks=ended_by_lower_bound + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_COMPOUND_LITERAL_INIT +// POS_COMPOUND_LITERAL_INIT: -fbounds-safety-bringup-missing-checks=compound_literal_init + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=libc_attributes -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_LIBC_ATTRIBUTES +// POS_LIBC_ATTRIBUTES: -fbounds-safety-bringup-missing-checks=libc_attributes + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=all -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ALL +// POS_ALL: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ACCESS +// NEG_ACCESS: -fno-bounds-safety-bringup-missing-checks=access_size + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_INDIRECT +// NEG_INDIRECT: -fno-bounds-safety-bringup-missing-checks=indirect_count_update + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_RETURN +// NEG_RETURN: -fno-bounds-safety-bringup-missing-checks=return_size + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ENDED_BY_LOWER +// NEG_ENDED_BY_LOWER: -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_COMPOUND_LITERAL_INIT +// NEG_COMPOUND_LITERAL_INIT: -fno-bounds-safety-bringup-missing-checks=compound_literal_init + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=libc_attributes -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_LIBC_ATTRIBUTES +// NEG_LIBC_ATTRIBUTES: -fno-bounds-safety-bringup-missing-checks=libc_attributes + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ALL +// NEG_ALL: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_COMMA +// POS_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_COMMA +// NEG_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefixes=POS_NEG_COMMA +// POS_NEG_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all +// POS_NEG_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefixes=NEG_POS_COMMA +// NEG_POS_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all +// NEG_POS_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_NEG +// UNUSED_NEG: warning: argument unused during compilation: '-fno-bounds-safety-bringup-missing-checks' + +// RUN: %clang -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_POS +// UNUSED_POS: warning: argument unused during compilation: '-fbounds-safety-bringup-missing-checks' + diff --git a/clang/test/BoundsSafety/Driver/cmdl_opts.c b/clang/test/BoundsSafety/Driver/cmdl_opts.c new file mode 100644 index 0000000000000..57ad53b511fde --- /dev/null +++ b/clang/test/BoundsSafety/Driver/cmdl_opts.c @@ -0,0 +1,8 @@ + +// Making sure the Frontend accepts the -fbounds-safety flags - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -fbounds-safety -fsyntax-only %s + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Driver/driver.c b/clang/test/BoundsSafety/Driver/driver.c new file mode 100644 index 0000000000000..3bb1d1cfff11d --- /dev/null +++ b/clang/test/BoundsSafety/Driver/driver.c @@ -0,0 +1,59 @@ +// ALL-NOT: unknown argument + +// Warning: careful to not grep some directory in the buid +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-safety + +// RUN: %clang -fbounds-safety -### %s 2>&1 | FileCheck -check-prefixes ALL,T0 %s +// T0: -fbounds-safety +// T0: -enable-constraint-elimination + +// RUN: %clang -fbounds-safety -fno-bounds-safety -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination + +// RUN: %clang -fno-bounds-safety -fbounds-safety -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T4 %s +// T4: -fbounds-safety +// T4: -enable-constraint-elimination + +// RUN: %clang -fbounds-attributes -c %s -### 2>&1 | not grep fbounds-attributes +// RUN: %clang -fbounds-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T6 %s +// T6: -fbounds-safety +// T6: -enable-constraint-elimination + +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-attributes -e enable-constraint-elimination +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination + + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T7 %s +// T7: 1 + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination=false -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T8 %s +// T8: 1 + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination=false -### %s 2>&1 | FileCheck -check-prefixes ALL,T9 %s +// T9: -enable-constraint-elimination=false + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination -mllvm -enable-constraint-elimination=false -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T10 %s +// T10: 2 + +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -### %s 2>&1 | FileCheck -check-prefixes ALL,T11 %s +// T11: -fbounds-attributes-cxx-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -fno-bounds-attributes-cxx-experimental -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental + +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -### %s 2>&1 | FileCheck -check-prefixes ALL,T12 %s +// T12: -fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -fno-bounds-attributes-objc-experimental -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T13 %s +// T13: -fbounds-safety +// T13: -enable-constraint-elimination + +// RUN: %clang -fbounds-safety -fno-bounds-safety-relaxed-system-headers -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T14 %s +// T14: -fno-bounds-safety-relaxed-system-headers diff --git a/clang/test/BoundsSafety/Driver/only_c_is_supported.c b/clang/test/BoundsSafety/Driver/only_c_is_supported.c new file mode 100644 index 0000000000000..09219a91901d8 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/only_c_is_supported.c @@ -0,0 +1,8 @@ + +// RUN: not %clang -fbounds-safety -x c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -fbounds-safety -x objective-c %s 2>&1 | FileCheck %s + +// RUN: not %clang -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Driver/trap-function-may-return.c b/clang/test/BoundsSafety/Driver/trap-function-may-return.c new file mode 100644 index 0000000000000..0bba0964057bb --- /dev/null +++ b/clang/test/BoundsSafety/Driver/trap-function-may-return.c @@ -0,0 +1,12 @@ + +// RUN: %clang -ftrap-function=foo -ftrap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD-BE-THERE %s +// RUN: %clang -ftrap-function=foo -fno-trap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD %s + +// RUN: %clang -ftrap-function=foo -fno-trap-function-returns -ftrap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD-BE-THERE %s +// RUN: %clang -ftrap-function=foo -ftrap-function-returns -fno-trap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD %s + +// RUN: not %clang -fsyntax-only -ftrap-function-returns %s 2>&1 > /dev/null +// RUN: %clang -fsyntax-only -fno-trap-function-returns %s 2>&1 > /dev/null + +// SHOULD-BE-THERE: -ftrap-function-returns +// SHOULD-NOT: -ftrap-function-return diff --git a/clang/test/BoundsSafety/FixIt/autobound-pointers.c b/clang/test/BoundsSafety/FixIt/autobound-pointers.c new file mode 100644 index 0000000000000..510332a1d90ad --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/autobound-pointers.c @@ -0,0 +1,161 @@ +// RUN: cp %s %t +// RUN: not %clang_cc1 -fbounds-safety -fdiagnostics-parseable-fixits -fixit -fix-what-you-can %t > %t.cc_out 2> %t.cc_out +// RUN: grep -v FIXIT-CHECK %t | FileCheck --check-prefix=FIXIT-CHECK %s +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +struct foo; +#define FOOP struct foo * +typedef struct foo *foop; + +void take_single(struct foo **); + +void no_fixits(void) { + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{12: + // FIXIT-CHECK: struct foo *no_fixit; + struct foo *no_fixit; // No Fix + take_single(&no_fixit); +} + +#include + +void take_indexable(struct foo *__indexable *); +void take_bidi_indexable(struct foo *__bidi_indexable *); +void take_unsafe_indexable(struct foo *__unsafe_indexable *); + +void fixits(void) { + // FIXIT-CHECK: struct foo *__single t_single; + struct foo *t_single; // Fix + // FIXIT-CHECK: struct foo *__indexable t_idx; + struct foo *t_idx; // Fix + // FIXIT-CHECK: struct foo *t_bidi; + struct foo *t_bidi; // No Fix + // FIXIT-CHECK: struct foo *__unsafe_indexable t_unsafe; + struct foo *t_unsafe; + take_single(&t_single); + take_indexable(&t_idx); + take_bidi_indexable(&t_bidi); + take_unsafe_indexable(&t_unsafe); + + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__single " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__indexable " + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__bidi_indexable " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__unsafe_indexable " + // FIXIT-CHECK: FOOP __single t_single_macro; + FOOP t_single_macro; // Fix + // FIXIT-CHECK: FOOP __indexable t_idx_macro; + FOOP t_idx_macro; // Fix + // FIXIT-CHECK: FOOP t_bidi_macro; + FOOP t_bidi_macro; // No Fix + // FIXIT-CHECK: FOOP __unsafe_indexable t_unsafe_macro; + FOOP t_unsafe_macro; // Fix + take_single(&t_single_macro); + take_indexable(&t_idx_macro); + take_bidi_indexable(&t_bidi_macro); + take_unsafe_indexable(&t_unsafe_macro); + + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__single " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__indexable " + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__bidi_indexable " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__unsafe_indexable " + // FIXIT-CHECK: foop __single take_single_td; + foop take_single_td; // Fix + // FIXIT-CHECK: foop __indexable take_idx_td; + foop take_idx_td; // Fix + // FIXIT-CHECK: foop take_bidi_td; + foop take_bidi_td; // No Fix + // FIXIT-CHECK: foop __unsafe_indexable take_unsafe_td; + foop take_unsafe_td; + take_single(&take_single_td); + take_indexable(&take_idx_td); + take_bidi_indexable(&take_bidi_td); + take_unsafe_indexable(&take_unsafe_td); + + // FIXIT-CHECK: struct foo * __single t_single2; + struct foo * t_single2; // Fix + // FIXIT-CHECK: struct foo * __indexable t_idx2; + struct foo * t_idx2; // Fix + // FIXIT-CHECK: struct foo * t_bidi2; + struct foo * t_bidi2; // No Fix + // FIXIT-CHECK: struct foo * __unsafe_indexable t_unsafe2; + struct foo * t_unsafe2; // Fix + take_single(&t_single2); + take_indexable(&t_idx2); + take_bidi_indexable(&t_bidi2); + take_unsafe_indexable(&t_unsafe2); + + // FIXIT-CHECK: struct foo* __single t_single3; + struct foo* t_single3; // Fix + // FIXIT-CHECK: struct foo* __indexable t_idx3 + struct foo* t_idx3; // Fix + // FIXIT-CHECK: struct foo* t_bidi3; + struct foo* t_bidi3; // No Fix + // FIXIT-CHECK: struct foo* __unsafe_indexable t_unsafe3; + struct foo* t_unsafe3; // Fix + take_single(&t_single3); + take_indexable(&t_idx3); + take_bidi_indexable(&t_bidi3); + take_unsafe_indexable(&t_unsafe3); +} + +void multiple_fixes_to_decl(void) { + // "DPF" checks (diagnostics-parseable-fixits) are used to check that the other fixits + // that don't get applied automatically are emitted in someway. + + // FIXIT-CHECK: struct foo* __single mftd_single; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__single " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__single " + struct foo* mftd_single; // Fix + // FIXIT-CHECK: struct foo* __indexable mftd_idx; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__indexable " + struct foo* mftd_idx; // Fix + // FIXIT-CHECK: struct foo* mftd_bidi; + struct foo* mftd_bidi; // No Fix + // FIXIT-CHECK: struct foo* __unsafe_indexable mftd_unsafe; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__unsafe_indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__unsafe_indexable " + struct foo* mftd_unsafe; // Fix + // FIXIT-CHECK: struct foo* __single mftd_mixed; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+3]]:17-[[@LINE+3]]:17}:"__single " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__unsafe_indexable " + struct foo* mftd_mixed; // Fix + + take_single(&mftd_single); + take_single(&mftd_single); + + take_indexable(&mftd_idx); + take_indexable(&mftd_idx); + + take_bidi_indexable(&mftd_bidi); + take_bidi_indexable(&mftd_bidi); + + take_unsafe_indexable(&mftd_unsafe); + take_unsafe_indexable(&mftd_unsafe); + + // In this case the first call is used to generate the fix-it that + // is automatically. + take_single(&mftd_mixed); + take_indexable(&mftd_mixed); + take_bidi_indexable(&mftd_mixed); + take_unsafe_indexable(&mftd_mixed); +} + +void take_counted_by(struct foo *__sized_by(*size) *, int *size); + +struct foo *glob; + +void no_fixits_2(void) { + // FIXIT-CHECK: struct foo *__bidi_indexable i; + struct foo *__bidi_indexable i; // No Fix + take_single(&i); + + struct foo *j; + int size = 0; + take_counted_by(&j, &size); + + int *k; + take_single(&k); + + struct foo *__unsafe_indexable *l = &glob; +} diff --git a/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c b/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c new file mode 100644 index 0000000000000..6c3db80e1f695 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c @@ -0,0 +1,218 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck %s --input-file=%t.cc_out + +#include + +// expected-note@+1 10{{passing argument to parameter 'path' here}} +void method_with_single_const_param(const char *path); + +// expected-note@+1 10{{passing argument to parameter 'path' here}} +void method_with_single_null_terminated_param(char * __null_terminated path); + +void test_single_invocation_local_variable() { + + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated" + char array_of_chars[] = "foo"; + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[] = "foo"; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +// expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:21-[[@LINE+1]]:21}:"__null_terminated" +char array_of_chars[] = "foo"; + +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:22-[[@LINE+2]]:22}:"__null_terminated" +// expected-note@+1{{consider adding '__null_terminated' to 'array_of_chars2'}} +char array_of_chars2[] = "foo"; + +void test_single_invocation_global_variable() { + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_explicit_const_size_arrays() { + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated " + char array_of_chars[20] = "foo"; + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[20] = "foobar"; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[20]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[20]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_initialized_local_arrays_without_null_termination() { + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated " + char array_of_chars[] = {'f', 'o', 'o'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars2[] = {'f', 'o', 'o', 'b', 'a', 'r'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_initialized_local_arrays_with_null_termination() { + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated" + char array_of_chars[] = {'f', 'o', 'o', '\0'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[] = {'f', 'o', 'o', 'b', 'a', 'r', '\0'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[7]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); + + char array_of_chars3[6][2] = {{'f'}, {'o'}, {'x', 'y'}, {'b', 'a'}, {'r', '\0'}}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[6][2]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars3); + + char array_of_chars4[6][2] = {{'f'}, {'o'}, {'x', 'y'}, {'b', 'a'}, {'r', '\0'}}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6][2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + +} + +typedef char my_array_t[4]; +// expected-note@+2{{consider adding '__null_terminated' to 'typedef_array1'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__null_terminated " +my_array_t typedef_array1 = "foo"; + +// expected-note@+2{{consider adding '__null_terminated' to 'typedef_array2'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__null_terminated " +my_array_t typedef_array2 = "bar"; + +void test_typedefed_arrays() { + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1{{passing 'my_array_t' (aka 'char[4]') to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(typedef_array1); + + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1{{passing 'my_array_t' (aka 'char[4]') to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(typedef_array2); + +} + +// expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars123'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:24-[[@LINE+1]]:24}:"__null_terminated " +char array_of_chars123[10]; + +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:24-[[@LINE+2]]:24}:"__null_terminated " +// expected-note@+1{{consider adding '__null_terminated' to 'array_of_chars345'}} +char array_of_chars345[6]; + +void test_uninitialized_global_arrays() { + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[10]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars123); + + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars345); +} + +void test_no_fixits() { + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__null_terminated " + const char array_of_chars1[] = "foo"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars2[__null_terminated] = "bar"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars3[__null_terminated 20] = "bar"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars4[3] = "foo"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars5[3] = {}; + + // expected-warning@+1 {{passing 'const char[4]' to parameter of type 'char *__single __terminated_by(0)' (aka 'char *__single') discards qualifiers}} + method_with_single_null_terminated_param(array_of_chars1); + + method_with_single_const_param(array_of_chars1); + + method_with_single_null_terminated_param(array_of_chars2); + + method_with_single_const_param(array_of_chars2); + + method_with_single_null_terminated_param(array_of_chars3); + + method_with_single_const_param(array_of_chars3); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars4); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + +} diff --git a/clang/test/BoundsSafety/FixIt/fixit-function-decl.c b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c new file mode 100644 index 0000000000000..06073aee338ee --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c @@ -0,0 +1,717 @@ +// RUN: %clang_cc1 -fbounds-safety -fblocks -verify -verify-ignore-unexpected=note %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -fbounds-safety -fixit -fix-what-you-can %t 2> /dev/null +// RUN: grep -v CHECK %t | FileCheck %s + +#include + +// CHECK: F0RT __bidi_indexable f0(void); +// CHECK: int *__bidi_indexable f0(void); +// expected-error@+3{{conflicting types for 'f0'}} +#define F0RT int * +F0RT f0(void); +int *__bidi_indexable f0(void); + +// CHECK: F1RT f1(void); +// CHECK: int *__bidi_indexable *f1(void); +// expected-error@+3{{conflicting types for 'f1'}} +#define F1RT int ** +F1RT f1(void); // no fixit: can't add bidi_indexable in the middle +int *__bidi_indexable *f1(void); + +// CHECK: int f2(void); +// CHECK: int *__bidi_indexable *f2(void); +// expected-error@+2{{conflicting types for 'f2'}} +int f2(void); // no fixit: can't change from non-pointer to pointer type +int *__bidi_indexable *f2(void); + +// CHECK: int *__bidi_indexable f3(void); +// CHECK: int *__bidi_indexable f3(void); +// expected-error@+2{{conflicting types for 'f3'}} +int *f3(void); +int *__bidi_indexable f3(void); + +// CHECK: f4rt __bidi_indexable f4(void); +// CHECK: int *__bidi_indexable f4(void); +// expected-error@+3{{conflicting types for 'f4'}} +typedef int *f4rt; +f4rt f4(void); +int *__bidi_indexable f4(void); + +// CHECK: f5rt f5(void); +// CHECK: int *__bidi_indexable *f5(void); +// expected-error@+3{{conflicting types for 'f5'}} +typedef int **f5rt; +f5rt f5(void); // no fixit: can't add bidi_indexable in the middle +int *__bidi_indexable *f5(void); + +// CHECK: void f6(int *b, int a); +// CHECK: void f6(int a, int *__counted_by(a) b); +// expected-error@+2{{conflicting types for 'f6'}} +void f6(int *b, int a); +void f6(int a, int *__counted_by(a) b); // no fixit: swapped arguments + +// CHECK: void f7(int *a); +// CHECK: void f7(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f7(int *a); +void f7(int *__counted_by(b) a, int b); // no fixit: added argument + +// CHECK: void f8(int *a, int b, int c); +// CHECK: void f8(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f8(int *a, int b, int c); +void f8(int *__counted_by(b) a, int b); // no fixit: removed argument + +// CHECK: void f9(int *__counted_by(b) a, int b); +// CHECK: void f9(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f9(int *a, int b); +void f9(int *__counted_by(b) a, int b); + +// CHECK: void f10(int *a, int b); +// CHECK: void f10(int *__counted_by(hello) a, int hello); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f10(int *a, int b); +void f10(int *__counted_by(hello) a, int hello); // TODO: no fixit because variable name changed :( + +// CHECK: void f11(int *a, int b, int c); +// CHECK: void f11(int *__counted_by(hello + world) a, int hello, int world); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f11(int *a, int b, int c); +void f11(int *__counted_by(hello + world) a, int hello, int world); // TODO: no fixit because variable name changed :( + +// CHECK: void f12(int *__counted_by(b + c) a, int b, int c); +// CHECK: void f12(int *__counted_by(b + c) a, int b, int c); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f12(int *a, int b, int c); +void f12(int *__counted_by(b + c) a, int b, int c); + +// CHECK: int *__counted_by(count) f13(int count); +// CHECK: int *__counted_by(count) f13(int count); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int *f13(int count); +int *__counted_by(count) f13(int count); + +// CHECK: void f14(int *__sized_by(b) a, int b); +// CHECK: void f14(int *__sized_by(b) a, int b); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +void f14(int *a, int b); +void f14(int *__sized_by(b) a, int b); + +// CHECK: void f15(int *__sized_by(b + c) a, int b, int c); +// CHECK: void f15(int *__sized_by(b + c) a, int b, int c); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +void f15(int *a, int b, int c); +void f15(int *__sized_by(b + c) a, int b, int c); + +// CHECK: int *__sized_by(count) f16(int count); +// CHECK: int *__sized_by(count) f16(int count); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +int *f16(int count); +int *__sized_by(count) f16(int count); + +// CHECK: void f17(int *__ended_by(b) a, int *b); +// CHECK: void f17(int *__ended_by(b) a, int *b); +// expected-error@+2{{conflicting '__ended_by' attribute with the previous function declaration}} +void f17(int *a, int *b); +void f17(int *__ended_by(b) a, int *b); + +// CHECK: int *f18(int *a); +// CHECK: typeof(f18) f18; +int *f18(int *a); +typeof(f18) f18; + +// CHECK: int *__counted_by(b) f19(int *__counted_by(b) a, int b); +// CHECK: typeof(f19) f20; +// CHECK: int *__counted_by(b) f20(int *__counted_by(b) a, int b); +// expected-error@+3{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(b) f19(int *__counted_by(b) a, int b); +typeof(f19) f20; +int *f20(int *a, int b); + +// CHECK: void f21(int a, int *__counted_by(a) b); +// CHECK: void f21(int a, int *__counted_by(a) b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f21(int a, int *b); +void f21(int a, int *__counted_by(a) b); + +// CHECK: void f22(F0PT __bidi_indexable p); +// CHECK: void f22(int *__bidi_indexable p); +// expected-error@+3{{conflicting types for 'f22'}} +#define F0PT int * +void f22(F0PT p); +void f22(int *__bidi_indexable p); + +// CHECK: void f23(F1PT p); +// CHECK: void f23(int *__bidi_indexable *p); +// expected-error@+3{{conflicting types for 'f23'}} +#define F1PT int ** +void f23(F1PT p); // no fixit: can't add bidi_indexable in the middle +void f23(int *__bidi_indexable *p); + +// CHECK: void f24(int *__indexable p); +// CHECK: void f24(int *__indexable p); +// expected-error@+2{{conflicting types for 'f24'}} +void f24(int *p); +void f24(int *__indexable p); + +// CHECK: fptr_t f25; +// CHECK: void f25(int *__bidi_indexable p); +// expected-error@+3{{conflicting types for 'f25'}} +typedef void fptr_t(int *p); +fptr_t f25; +void f25(int *__bidi_indexable p); + +// CHECK: void f26(int *__bidi_indexable p); +// CHECK: void f26(int *__bidi_indexable p) { +// expected-error@+2{{conflicting types for 'f26'}} +void f26(int *p); +void f26(int *__bidi_indexable p) { + return; +} + +// CHECK: void f27(int *__bidi_indexable p); +// CHECK: void f27(int *__bidi_indexable p); +// expected-error@+2{{conflicting types for 'f27'}} +void f27(int *__bidi_indexable p); +void f27(int *p); + +// CHECK: int* __bidi_indexable f28(void); +// CHECK: int* __bidi_indexable f28(void); +// expected-error@+2{{conflicting types for 'f28'}} +int* __bidi_indexable f28(void); +int* f28(void); + +// CHECK: int* __counted_by(count) f29(int count); +// CHECK: int* __counted_by(count) f29(int count); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int* __counted_by(count) f29(int count); +int* f29(int count); + +// CHECK: int f30(int* __bidi_indexable b); +// CHECK: int f30(int* __bidi_indexable b); +// expected-error@+2{{conflicting types for 'f30'}} +int f30(int* __bidi_indexable b); +int f30(int* b); + +// CHECK: int f31(int* __bidi_indexable); +// CHECK: int f31(int*__bidi_indexable); +// expected-error@+2{{conflicting types for 'f31'}} +int f31(int* __bidi_indexable); +int f31(int*); + +// CHECK: int f32(int *__bidi_indexable buffer); +// CHECK: int f32(int *__bidi_indexable buffer); +// expected-error@+2{{conflicting types for 'f32'}} +int f32(int *__bidi_indexable buffer); +int f32(int *buffer); + +// CHECK: int f33(int* __bidi_indexable buffer); +// CHECK: int f33(int* __bidi_indexable buffer); +// expected-error@+2{{conflicting types for 'f33'}} +int f33(int* __bidi_indexable buffer); +int f33(int* buffer); + +// CHECK: int f34(int *__counted_by(size) buffer, int size); +// CHECK: int f34(int *__counted_by(size) buffer, int size); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int f34(int *__counted_by(size) buffer, int size); +int f34(int *buffer, int size); + +// CHECK: int f35(int *nullable __bidi_indexable foo); +// CHECK: int f35(int *nullable __bidi_indexable foo); +// expected-error@+3{{conflicting types for 'f35'}} +#define nullable _Nullable +int f35(int *nullable foo); +int f35(int *nullable __bidi_indexable foo); + +// CHECK: int f36(int *nullable __bidi_indexable foo); +// CHECK: int f36(int *__bidi_indexable nullable foo); +// expected-error@+3{{conflicting types for 'f36'}} +#define nullable _Nullable +int f36(int *nullable foo); +int f36(int *__bidi_indexable nullable foo); + +// CHECK: int f37(int *__bidi_indexable const foo); +// CHECK: int f37(int *const __bidi_indexable foo); +// expected-error@+2{{conflicting types for 'f37'}} +int f37(int *const foo); +int f37(int *const __bidi_indexable foo); + +// CHECK: int f38(int *__bidi_indexable const foo); +// CHECK: int f38(int *__bidi_indexable const foo); +// expected-error@+2{{conflicting types for 'f38'}} +int f38(int *const foo); +int f38(int *__bidi_indexable const foo); + +// CHECK: int f39(int *__bidi_indexable foo); +// CHECK: int f39(int *my_bidi foo); +// expected-error@+3{{conflicting types for 'f39'}} +#define my_bidi __bidi_indexable +int f39(int *foo); +int f39(int *my_bidi foo); + +// CHECK: int *__bidi_indexable f40(int *__indexable p); +// CHECK: int *__bidi_indexable f40(int *__indexable p); +// expected-error@+2{{conflicting types for 'f40'}} +int *f40(int *p); +int *__bidi_indexable f40(int *__indexable p); + +//============================================================================== +// _Nonnull attribute on pointers +// +// The expected application of fix-its below +// +// * Doesn't try to match mismatched `_Nonnull` attributes +// * Demonstrates several cases where fix-its fail to apply but should (rdar://116016096) +// * Demonstrates several cases where a fix-it is applied but creates broken code (rdar://116016096) +// +// The declarations below were programmatically generated from a +// configuration matrix of: +// +// * Attribute is on parameter or return type +// * Parameter name attribute is on is specified +// * Attribute appears only on first or second decl +// * Attribute name (__counted_by and __bidi_indexable were used below) +// * Attribute is on inner or outer pointer +// * _Nonnull is on the destination type for the fix +// * _Nonnull is on the source type which the fix is derived from +//============================================================================== + + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); + +// This catches a crash (rdar://115575540) where the `_Nonnull` +// attribute wasn't handled. +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int ** buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int ** buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int ** buffer); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int ** __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int ** __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int ** buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_src(int **__counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_src(int **, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_src(int **, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__bidi_indexable ); +void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__bidi_indexable ); +void f_on_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull); + +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_src(int **__bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_src(int **); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +void f_on_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_src(int **); +void f_on_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_src(int **); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_src(int **__counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_src(int **, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__bidi_indexable ); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__bidi_indexable ); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable); + +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_src(int **__bidi_indexable); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_src(int **); +void f_on_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +void f_on_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable*); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_src(int **); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_src(int **); +void f_on_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +// CHECK: int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +int ** f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +// CHECK: int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +int ** f_on_ret_attr_fd_b_outer_ptr_nn_src(void); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +// CHECK: int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +int ** f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +// CHECK: int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +int ** f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: int * __bidi_indexable* f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +int * __bidi_indexable* f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +// CHECK: int ** f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +int ** f_on_ret_attr_fd_b_inner_ptr_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +// CHECK: int * __bidi_indexable* f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +int * __bidi_indexable* f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: int ** f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +int ** f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_src(void); diff --git a/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c new file mode 100644 index 0000000000000..53eace1137621 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c @@ -0,0 +1,289 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +#include +#include + +// expected-note@+1 2{{passing argument to parameter here}} +size_t my_strlen(const char* __null_terminated); +int my_memcmp(const void*__sized_by(n) s1, const void *__sized_by(n) s2, size_t n); + +typedef int proc_t; + +const char* cs_identity_get(proc_t proc); + +#define SIGNING_ID "signing_id" +#define SIGNING_ID_LEN (sizeof(SIGNING_ID) - 1) + +void call(proc_t proc) { + // expected-note@+2 2{{consider adding '__null_terminated' to 'signing_id'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__null_terminated " + const char *signing_id = NULL; + // ========================================================================= + // error: assigning to 'const char *__bidi_indexable' from incompatible type + // 'const char *__single __terminated_by(0)' (aka 'const char *__single') + // requires a linear search for the terminator; use + // '__terminated_by_to_indexable()' to perform this conversion explicitly + // + // **NOTE**: This is __null_terminated -> __bidi conversion + // + // --- + // Multiple choice fix-its (each to be emitted on a separate note) + // 1. Use builtin to perform conversion: + // ``` + // (signing_id = __null_terminated_to_indexable(cs_identity_get(proc))) + // ``` + // Note `__terminated_by_to_indexable` should be suggested instead if the thing we want to convert is a __terminated_by that's + // not a __null_terminated. + // + // 2. Add `__null_terminated` to the declaration of the local. + // + // ``` + // const char *__null_terminated signing_id = NULL; + // ``` + // + // Note: this breaks the call to `my_memcmp` which is why this a fix-it on a note (low confidence fix-it), + // rather than the top-level diagnostic (high-confidence fix-it). + // ========================================================================= + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:18-[[@LINE+5]]:18}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:39-[[@LINE+4]]:39}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:18-[[@LINE+2]]:18}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:39-[[@LINE+1]]:39}:")" + signing_id = cs_identity_get(proc); + // ========================================================================= + // error: passing 'const char *__bidi_indexable' to parameter of + // incompatible type 'const char *__single __terminated_by(0)' + // (aka 'const char *__single') is an unsafe operation; + // use '__unsafe_terminated_by_from_indexable()' or + // '__unsafe_forge_terminated_by()' to perform this conversion + // + // **NOTE**: This is __bidi conversion -> __null_terminated conversion + // --- + // Multiple choice fix-its (each to be emitted on a separate note) + // + // 1. Use O(N) search builtin + // + // my_strlen(__unsafe_null_terminated_from_indexable(signing_id)) + // + // 2. Use O(1) search builtin + // + // my_strlen(__unsafe_null_terminated_from_indexable(signing_id, <# pointer to null terminator #>)) + // + // 3. Add `__null_terminated` to the declaration of the local. + // + // ``` + // const char *__null_terminated signing_id = NULL; + // ``` + // ========================================================================= + // expected-error@+7{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:20-[[@LINE+5]]:20}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:30-[[@LINE+4]]:30}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:20-[[@LINE+2]]:20}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:30-[[@LINE+1]]:30}:", <# pointer to null terminator #>)" + if ((my_strlen(signing_id) == SIGNING_ID_LEN)) + return; + if (my_memcmp(signing_id, SIGNING_ID, SIGNING_ID_LEN) == 0) + return; + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'var_init'}} + // expected-note@+7{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+6]]:15-[[@LINE+6]]:15}:"__null_terminated " + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:26-[[@LINE+5]]:26}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:47-[[@LINE+4]]:47}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:26-[[@LINE+2]]:26}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:47-[[@LINE+1]]:47}:")" + const char *var_init = cs_identity_get(proc); +} + +const char *__bidi_indexable test_ret_null_to_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +__ptrcheck_abi_assume_bidi_indexable() +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi'}} +// DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +__ptrcheck_abi_assume_single() + +const char *__null_terminated test_ret_bidi_to_null() { + // expected-note@+2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:"__null_terminated " + const char *local = "bidi_local"; + // expected-error@+7{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:", <# pointer to null terminator #>)" + return local; +} + +void test_cast_null_to_bidi(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'cast_var'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:26-[[@LINE+5]]:26}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:61-[[@LINE+4]]:61}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:26-[[@LINE+2]]:26}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:61-[[@LINE+1]]:61}:")" + const char *cast_var = (const char *)cs_identity_get(proc); +} + +void test_cast_bidi_to_null(const char *__bidi_indexable bidi_param) { + // expected-error@+7{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:46-[[@LINE+5]]:46}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:70-[[@LINE+4]]:70}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:46-[[@LINE+2]]:46}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:70-[[@LINE+1]]:70}:", <# pointer to null terminator #>)" + const char *__null_terminated null_local = (const char *)bidi_param; +} + +// Constant array in struct variant + +#define SIZE 4 +struct Foo { + // expected-note@+2{{consider adding '__null_terminated' to 'msg'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:20-[[@LINE+1]]:20}:"__null_terminated " + const char msg[SIZE]; + const char *msg_ptr; +}; + +void consumeFoo(struct Foo* f) { + // ========================================================================= + // error: passing 'const char[4]' to parameter of incompatible type + // 'const char *__single __terminated_by(0)' (aka 'const char *__single') + // is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or + // '__unsafe_forge_terminated_by()' to perform this conversion + // + // **NOTE**: This is __bidi conversion -> __null_terminated conversion + // --- + // + // Multiple choice fix-its (each to be emitted on a separate note) + // + // 1. Use O(N) search builtin + // + // if (my_strlen(__unsafe_null_terminated_from_indexable(f->msg))) + // + // 2. Use O(1) search builtin + // + // if (my_strlen(__unsafe_null_terminated_from_indexable(f->msg, <# pointer to null terminator #>))) + // + // 3. Add `__null_terminated` to the declaration of the constant array + // + // ``` + // struct Foo { + // const char msg[__null_terminated SIZE]; + // }; + // ``` + // ========================================================================= + // expected-error@+7{{passing 'const char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:19-[[@LINE+5]]:19}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:25-[[@LINE+4]]:25}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:19-[[@LINE+2]]:19}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:25-[[@LINE+1]]:25}:", <# pointer to null terminator #>)" + if (my_strlen(f->msg)) { + // do something + } + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'var_init'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+7]]:17-[[@LINE+7]]:17}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:28-[[@LINE+5]]:28}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:38-[[@LINE+4]]:38}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:28-[[@LINE+2]]:28}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:38-[[@LINE+1]]:38}:")" + const char *var_init = f->msg_ptr; + // expected-error@+7{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:18-[[@LINE+5]]:18}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:26-[[@LINE+4]]:26}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:18-[[@LINE+2]]:18}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:26-[[@LINE+1]]:26}:", <# pointer to null terminator #>)" + f->msg_ptr = var_init; +} + +void call1(proc_t proc) { + const char *__bidi_indexable signing_id = NULL; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:16-[[@LINE+5]]:16}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:37-[[@LINE+4]]:37}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:16-[[@LINE+2]]:16}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:37-[[@LINE+1]]:37}:")" + signing_id = cs_identity_get(proc); +} + +// expected-note@+1 {{passing argument to parameter here}} +void consume_null_param(const char * __null_terminated); +void test_implicit_bidi_to_null_param(void) { + // expected-note@+2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__null_terminated " + const char* local; + // expected-error@+7{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:24-[[@LINE+5]]:24}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:24-[[@LINE+2]]:24}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:", <# pointer to null terminator #>)" + consume_null_param((local)); +} + +// expected-note@+1 2{{passing argument to parameter here}} +void consume_long_null_param(long long int * __null_terminated); +void test_bitcast_bidi_to_null(void) { + // expected-note@+2 2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__null_terminated " + int* local; + // expected-error@+7{{passing 'long long *__bidi_indexable' to parameter of incompatible type 'long long *__single __terminated_by(0)' (aka 'long long *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:29-[[@LINE+5]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:51-[[@LINE+4]]:51}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:29-[[@LINE+2]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:51-[[@LINE+1]]:51}:", <# pointer to null terminator #>)" + consume_long_null_param((long long int*) local); // Fixit doesn't fix anything: rdar://122840377 + // expected-error@+7{{passing 'long long *__bidi_indexable' to parameter of incompatible type 'long long *__single __terminated_by(0)' (aka 'long long *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:29-[[@LINE+5]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:67-[[@LINE+4]]:67}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:29-[[@LINE+2]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:67-[[@LINE+1]]:67}:", <# pointer to null terminator #>)" + consume_long_null_param((long long int*)(long long int*) local); +} diff --git a/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c new file mode 100644 index 0000000000000..c149c36e2bf53 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c @@ -0,0 +1,177 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +#include + +typedef int proc_t; + +const char *cs_identity_get(proc_t proc); + +const char *test_ret_null_to_imp_bidi_ret_var(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8 2{{consider adding '__null_terminated' to 'ret'}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:21-[[@LINE+5]]:21}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:42-[[@LINE+4]]:42}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:21-[[@LINE+2]]:21}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:42-[[@LINE+1]]:42}:")" + const char *ret = cs_identity_get(proc); + // expected-error@+7{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:", <# pointer to null terminator #>)" + return ret; +} + +const char *test_ret_null_to_imp_bidi_ret_var_attributed(proc_t proc) { + const char *__null_terminated ret = cs_identity_get(proc); + return ret; +} + +const char *__bidi_indexable test_ret_null_to_imp_bidi_ret_var_attributed_explicit(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'ret'}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:21-[[@LINE+5]]:21}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:42-[[@LINE+4]]:42}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:21-[[@LINE+2]]:21}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:42-[[@LINE+1]]:42}:")" + const char *ret = cs_identity_get(proc); + return ret; +} + +__ptrcheck_abi_assume_bidi_indexable() + +// The DPF-CHECK-DAGs on `test_ret_null_to_bidi` test that all prototypes prior to its definition get fixits. +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc); + +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_bidi'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +const char *test_assign_null_to_imp_bidi(const char *__null_terminated foo) { + const char *bar[42]; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:12-[[@LINE+5]]:12}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:12-[[@LINE+2]]:12}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:")" + bar[1] = foo; + return bar[1]; +} + +const char *test_assign_null_to_imp_bidi_annotate(const char *__null_terminated foo) { + const char *bar[42]; + bar[1] = __null_terminated_to_indexable(foo); + return bar[1]; +} + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi(const char *__null_terminated foo) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:")" + return foo; +} + +// FIXME: rdar://125936876 +const char *test_ret_null_to_bidi(proc_t proc); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi_param'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi_param(const char *__null_terminated foo, const char *param) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:")" + return foo; +} + +#define CC_TY const char* + +CC_TY __null_terminated return_macro(void); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_return_macro'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:7-[[@LINE+1]]:7}:"__null_terminated " +CC_TY test_return_macro(void) { + const char *__null_terminated a = return_macro(); + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:11-[[@LINE+4]]:11}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:11-[[@LINE+1]]:11}:")" + return a; +} + +const char *test_ret_null_to_imp_bidi_no_fix(const char *__null_terminated foo) { + const char *bar[42]; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:12-[[@LINE+5]]:12}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:12-[[@LINE+2]]:12}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:")" + bar[1] = foo; + return 0; +} + +int *test_imp_single_ret_imp_bidi(); + +int *test_imp_single_ret_imp_bidi() { + int *__single local; + return local; +} + +__ptrcheck_abi_assume_single() + +int *test_imp_single_ret() { + int *local; + return local; +} + +int *__bidi_indexable test_bidi_ret() { + // expected-note@+1{{pointer 'local' declared here}} + int *__single local; + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'local'}} + return local; +} + +int *test_imp_single_ret_bidi() { + int *__bidi_indexable local; + return local; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c b/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c new file mode 100644 index 0000000000000..3ef59345a397a --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c @@ -0,0 +1,821 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fblocks -verify %t +// RUN: not %clang_cc1 -fbounds-safety -fixit -fix-what-you-can %t 2> /dev/null +// RUN: grep -v FIXIT-CHECK %t | FileCheck --check-prefix=FIXIT-CHECK %s +#include + +typedef struct F F_t; + +F_t* single_source(void); + +int void_ptr_single_sink(void* __single); + +// The MACRO_PTR_* macros should not be modifed by any FixIt +// FIXIT-CHECK: #define MACRO_PTR_UNSAFE_TY struct F* __unsafe_indexable +#define MACRO_PTR_UNSAFE_TY struct F* __unsafe_indexable +// FIXIT-CHECK: #define MACRO_PTR_IDX_TY struct F* __indexable +#define MACRO_PTR_IDX_TY struct F* __indexable +// FIXIT-CHECK: #define MACRO_PTR_BIDI_TY struct F* __bidi_indexable +#define MACRO_PTR_BIDI_TY struct F* __bidi_indexable +// FIXIT-CHECK: #define MACRO_PTR_SINGLE_TY struct F* __single +#define MACRO_PTR_SINGLE_TY struct F* __single +// FIXIT-CHECK: #define MACRO_PTR_TY struct F* +#define MACRO_PTR_TY struct F* +#define MACRO_TY struct F + +int opaque_init_assign(F_t* single_f, void* single_void, + void* __single explicit_single_void) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local' as '__single'}} + // expected-note@+2{{pointer 'oia_local' declared here}} + // FIXIT-CHECK: F_t* __single oia_local = single_f; + F_t* oia_local = single_f; // Fix + // FIXIT-CHECK: F_t* __single oia_local_nf = single_f; + F_t* __single oia_local_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local2' as '__single'}} + // expected-note@+2{{pointer 'oia_local2' declared here}} + // FIXIT-CHECK: F_t* __single oia_local2 = single_source(); + F_t* oia_local2 = single_source(); // Fix + // FIXIT-CHECK: F_t* __single oia_local2_nf = single_source(); + F_t* __single oia_local2_nf = single_source(); // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local3' as '__single'}} + // expected-note@+2{{pointer 'oia_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oia_local3 = single_f; + MACRO_PTR_TY oia_local3 = single_f; // Fix + // FIXIT-CHECK: MACRO_PTR_TY __single oia_local3_nf = single_f; + MACRO_PTR_TY __single oia_local3_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local4' as '__single'}} + // expected-note@+2{{pointer 'oia_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oia_local4 = single_f; + MACRO_TY* oia_local4 = single_f; // Fix + // FIXIT-CHECK: MACRO_TY* __single oia_local4_nf = single_f; + MACRO_TY* __single oia_local4_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local5' as '__single'}} + // expected-note@+2{{pointer 'oia_local5' declared here}} + // FIXIT-CHECK: void* __single oia_local5 = single_void; + void* oia_local5 = single_void; // Fix + // FIXIT-CHECK: void* __single oia_local5_nf = single_void; + void* __single oia_local5_nf = single_void; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local6' as '__single'}} + // expected-note@+2{{pointer 'oia_local6' declared here}} + // FIXIT-CHECK: void* __single oia_local6 = explicit_single_void; + void* oia_local6 = explicit_single_void; // Fix + // FIXIT-CHECK: void* __single oia_local6_nf = explicit_single_void; + void* __single oia_local6_nf = explicit_single_void; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local7' as '__single'}} + // expected-note@+2{{pointer 'oia_local7' declared here}} + // FIXIT-CHECK: void* _Nullable __single oia_local7 = explicit_single_void; + void* _Nullable oia_local7 = explicit_single_void; // Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_SINGLE_TY oia_local8 = single_f; + MACRO_PTR_SINGLE_TY oia_local8 = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local9' as '__single'}} + // expected-note@+2{{pointer 'oia_local9' declared here}}} + // FIXIT-CHECK: MACRO_PTR_BIDI_TY oia_local9 = single_f; + MACRO_PTR_BIDI_TY oia_local9 = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local10' as '__single'}} + // expected-note@+2{{pointer 'oia_local10' declared here}}} + // FIXIT-CHECK: MACRO_PTR_IDX_TY oia_local10 = single_f; + MACRO_PTR_IDX_TY oia_local10 = single_f; // No Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_UNSAFE_TY oia_local11 = single_f; + MACRO_PTR_UNSAFE_TY oia_local11 = single_f; // No Fix +} + +int opaque_assign(F_t* imp_single, void* single_void, + void* __single explicit_single_void) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local' as '__single'}} + // expected-note@+2{{pointer 'oa_local' declared here}} + // FIXIT-CHECK: F_t* __single oa_local = 0; + F_t* oa_local = 0; // Fix + oa_local = imp_single; + // FIXIT-CHECK: F_t* __single oa_local_nf = 0; + F_t* __single oa_local_nf = 0; // No Fix + oa_local_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local2' as '__single'}} + // expected-note@+2{{pointer 'oa_local2' declared here}} + // FIXIT-CHECK: F_t* __single oa_local2 = 0; + F_t* oa_local2 = 0; // Fix + oa_local2 = single_source(); + // FIXIT-CHECK: F_t* __single oa_local2_nf = 0; + F_t* __single oa_local2_nf = 0; // No Fix + oa_local2_nf = single_source(); + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local3' as '__single'}} + // expected-note@+2{{pointer 'oa_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oa_local3 = 0; + MACRO_PTR_TY oa_local3 = 0; // Fix + oa_local3 = imp_single; + // FIXIT-CHECK: MACRO_PTR_TY __single oa_local3_nf = 0; + MACRO_PTR_TY __single oa_local3_nf = 0; // No Fix + oa_local3_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local4' as '__single'}} + // expected-note@+2{{pointer 'oa_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oa_local4 = 0; + MACRO_TY* oa_local4 = 0; // Fix + oa_local4 = imp_single; + // FIXIT-CHECK: MACRO_TY* __single oa_local4_nf = 0; + MACRO_TY* __single oa_local4_nf = 0; // No Fix + oa_local4_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local5' as '__single'}} + // expected-note@+2{{pointer 'oa_local5' declared here}} + // FIXIT-CHECK: void* __single oa_local5 = 0; + void* oa_local5 = 0; // Fix + oa_local5 = single_void; + // FIXIT-CHECK: void* __single oa_local5_nf = 0; + void* __single oa_local5_nf = 0; // No Fix + oa_local5_nf = single_void; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local6' as '__single'}} + // expected-note@+2{{pointer 'oa_local6' declared here}} + // FIXIT-CHECK: void* __single oa_local6 = 0; + void* oa_local6 = 0; // Fix + oa_local6 = explicit_single_void; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local7' as '__single'}} + // expected-note@+2{{pointer 'oa_local7' declared here}} + // FIXIT-CHECK: void* _Nullable __single oa_local7; + void* _Nullable oa_local7; + oa_local7 = explicit_single_void; // Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_SINGLE_TY oa_local8; + MACRO_PTR_SINGLE_TY oa_local8; // No Fix + oa_local8 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local9' as '__single'}} + // expected-note@+2{{pointer 'oa_local9' declared here}}} + // FIXIT-CHECK: MACRO_PTR_BIDI_TY oa_local9; + MACRO_PTR_BIDI_TY oa_local9; // No Fix + oa_local9 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local10' as '__single'}} + // expected-note@+2{{pointer 'oa_local10' declared here}}} + // FIXIT-CHECK: MACRO_PTR_IDX_TY oa_local10; + MACRO_PTR_IDX_TY oa_local10; // No Fix + oa_local10 = imp_single; + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_UNSAFE_TY oa_local11; + MACRO_PTR_UNSAFE_TY oa_local11; // No Fix + oa_local11 = imp_single; + + // Test assignment with parentheses on LHS + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local12' as '__single'}} + // expected-note@+2{{pointer 'oa_local12' declared here}} + // FIXIT-CHECK: F_t* __single oa_local12 = 0; + F_t* oa_local12 = 0; // Fix + (oa_local12) = imp_single; +} + +int opaque_multiple_assignments(F_t* imp_single) { + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local' as '__single'}} + // expected-note@+4{{pointer 'oma_local' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local' as '__single'}} + // expected-note@+2{{pointer 'oma_local' declared here}} + // FIXIT-CHECK: F_t* __single oma_local = 0; + F_t* oma_local = 0; // Fix + oma_local = imp_single; + oma_local = single_source(); + + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local3' as '__single'}} + // expected-note@+4{{pointer 'oma_local3' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local3' as '__single'}} + // expected-note@+2{{pointer 'oma_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oma_local3 = 0; + MACRO_PTR_TY oma_local3 = 0; // Fix + oma_local3 = imp_single; + oma_local3 = single_source(); + + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local4' as '__single'}} + // expected-note@+4{{pointer 'oma_local4' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local4' as '__single'}} + // expected-note@+2{{pointer 'oma_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oma_local4 = 0; + MACRO_TY* oma_local4 = 0; // Fix + oma_local4 = imp_single; + oma_local4 = single_source(); +} + +int block_local_fn(void* __single param) { + int (^block_func)(void) = ^{ + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'block_local' as '__single'}} + // expected-note@+2{{pointer 'block_local' declared here}} + // FIXIT-CHECK: void* __single block_local = param; + void* block_local = param; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'block_local2' as '__single'}} + // expected-note@+2{{pointer 'block_local2' declared here}} + // FIXIT-CHECK: void* __single block_local2; + void* block_local2; + block_local2 = param; + + return void_ptr_single_sink(block_local); + }; + + return block_func(); +} + +__ptrcheck_abi_assume_bidi_indexable() +typedef struct StructWithBidiPtrOIFD { + // expected-note@+2{{pointer 'StructWithBidiPtrOIFD::oifd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single oifd_bidi_ptr; + void* oifd_bidi_ptr; +} StructWithBidiPtrOIFD_t; + +typedef struct StructWithBidiPtrOIFD2 { + // expected-note@+2{{pointer 'StructWithBidiPtrOIFD2::oifd_bidi_ptr2' declared here}} + // FIXIT-CHECK: void* __single oifd_bidi_ptr2; + void* oifd_bidi_ptr2; +} StructWithBidiPtrOIFD2_t; + +typedef struct StructWithBidiPtrOAFD { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD::oafd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr; + void* oafd_bidi_ptr; +} StructWithBidiPtrOAFD_t; + +typedef struct StructWithBidiPtrOAFD2 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD2::oafd_bidi_ptr2' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr2; + void* oafd_bidi_ptr2; +} StructWithBidiPtrOAFD2_t; + +typedef struct StructWithBidiPtrOAFD3 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD3::oafd_bidi_ptr3' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr3; + void* oafd_bidi_ptr3; +} StructWithBidiPtrOAFD3_t; + +typedef struct StructWithBidiPtrOAFD4 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD4::oafd_bidi_ptr4' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr4; + void* oafd_bidi_ptr4; +} StructWithBidiPtrOAFD4_t; + +typedef struct StructWithBidiPtrOMAFD { + // expected-note@+3{{pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' declared here}} + // expected-note@+2{{pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single omafd_bidi_ptr; + void* omafd_bidi_ptr; +} StructWithBidiPtrOMAFD_t; + + + +__ptrcheck_abi_assume_indexable(); + +typedef struct StructWithIdxPtrOIFD { + // expected-note@+2{{pointer 'StructWithIdxPtrOIFD::oifd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single oifd_idx_ptr; + void* oifd_idx_ptr; +} StructWithIdxPtrOIFD_t; + +typedef struct StructWithIdxPtrOIFD2 { + // expected-note@+2{{pointer 'StructWithIdxPtrOIFD2::oifd_idx_ptr2' declared here}} + // FIXIT-CHECK: void* __single oifd_idx_ptr2; + void* oifd_idx_ptr2; +} StructWithIdxPtrOIFD2_t; + +typedef struct StructWithIdxPtrOAFD { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD::oafd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr; + void* oafd_idx_ptr; +} StructWithIdxPtrOAFD_t; + +typedef struct StructWithIdxPtrOAFD2 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD2::oafd_idx_ptr2' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr2; + void* oafd_idx_ptr2; +} StructWithIdxPtrOAFD2_t; + +typedef struct StructWithIdxPtrOAFD3 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD3::oafd_idx_ptr3' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr3; + void* oafd_idx_ptr3; +} StructWithIdxPtrOAFD3_t; + +typedef struct StructWithIdxPtrOAFD4 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD4::oafd_idx_ptr4' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr4; + void* oafd_idx_ptr4; +} StructWithIdxPtrOAFD4_t; + +typedef struct StructWithIdxPtrOMAFD { + // expected-note@+2 2 {{pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single omafd_idx_ptr; + void* omafd_idx_ptr; +} StructWithIdxPtrOMAFD_t; + + +__ptrcheck_abi_assume_single() + +typedef struct Nested_Bidi_OIFD { + StructWithBidiPtrOIFD2_t field; +} Nested_Bidi_OIFD_t; + +typedef struct Nested_Idx_OIFD { + StructWithIdxPtrOIFD2_t field; +} Nested_Idx_OIFD_t; + +void opaque_init_field_decl(void* __single explicit_single) { + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOIFD::oifd_bidi_ptr' as '__single'}} + StructWithBidiPtrOIFD_t oifd_local = {.oifd_bidi_ptr = explicit_single }; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOIFD::oifd_idx_ptr' as '__single'}} + StructWithIdxPtrOIFD_t oifd_local2 = {.oifd_idx_ptr = explicit_single }; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOIFD2::oifd_bidi_ptr2' as '__single'}} + Nested_Bidi_OIFD_t oifd_local3 = {.field = {.oifd_bidi_ptr2 = explicit_single}}; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOIFD2::oifd_idx_ptr2' as '__single'}} + Nested_Idx_OIFD_t oifd_local4 = {. field = {.oifd_idx_ptr2 = explicit_single}}; +} + +typedef struct Nested_Bidi_OAFD { + StructWithBidiPtrOAFD3_t field; +} Nested_Bidi_OAFD_t; + +typedef struct Nested_Idx_OAFD { + StructWithIdxPtrOAFD3_t field; +} Nested_Idx_OAFD_t; + +void opaque_assign_field_decl(void* __single explicit_single, StructWithBidiPtrOAFD2_t* base_with_bidi_field, StructWithIdxPtrOAFD2_t* base_with_idx_field) { + StructWithBidiPtrOAFD_t oafd_local; + StructWithIdxPtrOAFD_t oafd_local2; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD::oafd_bidi_ptr' as '__single'}} + oafd_local.oafd_bidi_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD::oafd_idx_ptr' as '__single'}} + oafd_local2.oafd_idx_ptr = explicit_single; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD2::oafd_bidi_ptr2' as '__single'}} + base_with_bidi_field->oafd_bidi_ptr2 = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD2::oafd_idx_ptr2' as '__single'}} + base_with_idx_field->oafd_idx_ptr2 = explicit_single; + + Nested_Bidi_OAFD_t oafd_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD3::oafd_bidi_ptr3' as '__single'}} + oafd_local3.field.oafd_bidi_ptr3 = explicit_single; + + Nested_Idx_OAFD_t oafd_local4; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD3::oafd_idx_ptr3' as '__single'}} + oafd_local4.field.oafd_idx_ptr3 = explicit_single; + + // Test assignment with parentheses on LHS + StructWithBidiPtrOAFD4_t oafd_local5; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD4::oafd_bidi_ptr4' as '__single'}} + (oafd_local5.oafd_bidi_ptr4) = explicit_single; + + // Test assignment with parentheses on LHS + StructWithIdxPtrOAFD4_t oafd_local6; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD4::oafd_idx_ptr4' as '__single'}} + (oafd_local6.oafd_idx_ptr4) = explicit_single; +} + +void opaque_multiple_assign_field_decl(void* __single explicit_single) { + StructWithBidiPtrOMAFD_t omafd_local; + StructWithIdxPtrOMAFD_t omafd_local2; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' as '__single'}} + omafd_local.omafd_bidi_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' as '__single'}} + omafd_local.omafd_bidi_ptr = explicit_single; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' as '__single'}} + omafd_local2.omafd_idx_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' as '__single'}} + omafd_local2.omafd_idx_ptr = explicit_single; +} + + +// This defined to be a compile time constant because it seems this is the only +// thing clang with -fbounds-safety allows. +#define SINGLE_SOURCE_GLOBAL ((F_t* __single) 0) + +// Assigning single_source() isn't legal here because it isn't a constant. +// FIXIT-CHECK: F_t* global_F_single = SINGLE_SOURCE_GLOBAL; +F_t* global_F_single = SINGLE_SOURCE_GLOBAL; // No Fix + +__ptrcheck_abi_assume_bidi_indexable(); + +// It's questionable if the FixIts for these global assignments are of value. +// Once the fix is made compilation will fail because `__single_source()` isn't +// a compile time constant. + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi' as '__single'}} +// expected-note@+2{{pointer 'global_F_implicit_bidi' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi = single_source(); +F_t* global_F_implicit_bidi = single_source(); // Fix + +// expected-note@+2{{pointer 'global_F_implicit_bidi2' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi2; +F_t* global_F_implicit_bidi2; +void modify_global_implicit_bidi(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi2' as '__single'}} + global_F_implicit_bidi2 = explicit_single; +} + + +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_bidi3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_bidi3; +// expected-note@+3{{pointer 'global_F_implicit_bidi3' declared here}} +F_t* global_F_implicit_bidi3; // Fix +F_t* global_F_implicit_bidi3; // Fix +F_t* global_F_implicit_bidi3; // Fix +void modify_global_implicit_bidi2(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi3' as '__single'}} + global_F_implicit_bidi3 = explicit_single; +} + +// expected-note@+2 3 {{pointer 'global_F_implicit_bidi4' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi4; +F_t* global_F_implicit_bidi4; // Fix +void modify_global_implicit_bidi_multiple_assign(F_t* __single explicit_single) { + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + global_F_implicit_bidi4 = explicit_single; + global_F_implicit_bidi4 = explicit_single; + global_F_implicit_bidi4 = explicit_single; +} + +// This tests the case where a global gets a FixIt emitted on it and then it +// gets redeclared and then a FixIt gets emitted on the redeclaration. We have +// to be careful to not annotate the first declaration again. + +// expected-note@+2{{pointer 'global_F_implicit_bidi5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi5; +F_t* global_F_implicit_bidi5; // First Decl, Fix + +void modify_global_implicit_bidi_pre_redeclare(F_t* __single explicit_single) { + // Fixes First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi5' as '__single'}} + global_F_implicit_bidi5 = explicit_single; +} + +// expected-note@+2{{pointer 'global_F_implicit_bidi5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi5; +F_t* global_F_implicit_bidi5; // Second Decl, Fix + +void modify_global_implicit_bidi_post_redeclare(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi5' as '__single'}} + global_F_implicit_bidi5 = explicit_single; +} + +// FIXME(dliew): rdar://115456779 +// This is a case where not all the redeclarations can be fixed +// expected-note@+2{{pointer 'global_F_implicit_bidi6' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi6; +F_t* global_F_implicit_bidi6; // First Decl, Fix + +void modify_global_implicit_bidi_pre_redeclare2(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi6' as '__single'}} + global_F_implicit_bidi6 = explicit_single; +} + +// No note +// FIXIT-CHECK: F_t* global_F_implicit_bidi6; +F_t* global_F_implicit_bidi6; // Second Decl, No Fix + +__ptrcheck_abi_assume_indexable(); + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx' as '__single'}} +// expected-note@+2{{pointer 'global_F_implicit_idx' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx = single_source(); +F_t* global_F_implicit_idx = single_source(); // Fix + +// expected-note@+2{{pointer 'global_F_implicit_idx2' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx2; +F_t* global_F_implicit_idx2; +void modify_global_implicit_idx(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx2' as '__single'}} + global_F_implicit_idx2 = explicit_single; +} + +// FIXIT-CHECK: F_t* __single global_F_implicit_idx3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_idx3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_idx3; +// expected-note@+3{{pointer 'global_F_implicit_idx3' declared here}} +F_t* global_F_implicit_idx3; // Fix +F_t* global_F_implicit_idx3; // Fix +F_t* global_F_implicit_idx3; // Fix +void modify_global_implicit_idx2(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx3' as '__single'}} + global_F_implicit_idx3 = explicit_single; +} + +// expected-note@+2 3 {{pointer 'global_F_implicit_idx4' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx4; +F_t* global_F_implicit_idx4; // Fix +void modify_global_implicit_idx_multiple_assign(F_t* __single explicit_single) { + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + global_F_implicit_idx4 = explicit_single; + global_F_implicit_idx4 = explicit_single; + global_F_implicit_idx4 = explicit_single; +} + +// This tests the case where a global gets a FixIt emitted on it and then it +// gets redeclared and then a FixIt gets emitted on the redeclaration. We have +// to be careful to not annotate the first declaration again. + +// expected-note@+2{{pointer 'global_F_implicit_idx5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx5; +F_t* global_F_implicit_idx5; // First Decl, Fix + +void modify_global_implicit_idx_pre_redeclare(F_t* __single explicit_single) { + // Fixes First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx5' as '__single'}} + global_F_implicit_idx5 = explicit_single; +} + +// expected-note@+2{{pointer 'global_F_implicit_idx5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx5; +F_t* global_F_implicit_idx5; // Second Decl, Fix + +void modify_global_implicit_idx_post_redeclare(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx5' as '__single'}} + global_F_implicit_idx5 = explicit_single; +} + +// FIXME(dliew): rdar://115456779 +// This is a case where not all the redeclarations can be fixed +// expected-note@+2{{pointer 'global_F_implicit_idx6' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx6; +F_t* global_F_implicit_idx6; // First Decl, Fix + +void modify_global_implicit_idx_pre_redeclare2(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx6' as '__single'}} + global_F_implicit_idx6 = explicit_single; +} + +// No note +// FIXIT-CHECK: F_t* global_F_implicit_idx6; +F_t* global_F_implicit_idx6; // Second Decl, No Fix + + +__ptrcheck_abi_assume_single(); + +//============================================================================== +// FixIts on parameters +//============================================================================== + +__ptrcheck_abi_assume_bidi_indexable(); + +// expected-note@+2{{passing argument to parameter 'implicit_bidi' here}} +// FIXIT-CHECK: void implicit_bidi_sink(void* __single implicit_bidi); +void implicit_bidi_sink(void* implicit_bidi); // Fix + +// expected-note@+3{{pointer 'implicit_bidi2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter 'implicit_bidi2' here}} +// FIXIT-CHECK: void implicit_bidi_sink2(void* __single implicit_bidi2); +void implicit_bidi_sink2(void* implicit_bidi2); // Fix + +// expected-note@+3{{pointer declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter here}} +// FIXIT-CHECK: void implicit_bidi_sink3(void*__single); +void implicit_bidi_sink3(void*); // Fix + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_bidi_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi' parameter '__single'}} + implicit_bidi_sink(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + implicit_bidi_sink2(p); + implicit_bidi_sink2(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param3(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + implicit_bidi_sink3(p); + implicit_bidi_sink3(p); +} + +__ptrcheck_abi_assume_indexable(); + +// expected-note@+2{{passing argument to parameter 'implicit_idx_param' here}} +// FIXIT-CHECK: void implicit_idx_sink(void* __single implicit_idx_param); +void implicit_idx_sink(void* implicit_idx_param); // Fix + +// expected-note@+3{{pointer 'implicit_idx_param2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter 'implicit_idx_param2' here}} +// FIXIT-CHECK: void implicit_idx_sink2(void* __single implicit_idx_param2); +void implicit_idx_sink2(void* implicit_idx_param2); // Fix + +// expected-note@+3{{pointer declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter here}} +// FIXIT-CHECK: void implicit_idx_sink3(void*__single); +void implicit_idx_sink3(void*); // Fix + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_idx_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param' parameter '__single'}} + implicit_idx_sink(p); +} + +void single_opaque_arg_passed_to_implicit_idx_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param2' parameter '__single'}} + implicit_idx_sink2(p); + implicit_idx_sink2(p); +} + +void single_opaque_arg_passed_to_implicit_idx_param3(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the parameter '__single'}} + implicit_idx_sink3(p); + implicit_idx_sink3(p); +} + +//============================================================================== +// No FixIts for array assignments/initialization +// +// The lack of FixIts here is just an implementation limitation. +// rdar://115201001 +//============================================================================== + +__ptrcheck_abi_assume_bidi_indexable(); + +// FIXIT-CHECK: void* global_opaque_array_with_init[] = { single_source(), 0}; +// expected-error-re@+1{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'F_t *__single' (aka 'struct F *__single'){{$}}}} +void* global_opaque_array_with_init[] = { single_source(), 0}; // No Fix + +// FIXIT-CHECK: void* global_opaque_array[4]; +void* global_opaque_array[4]; // No Fix +__ptrcheck_abi_assume_single(); + +void assign_global_opaque_array(void* __single explicit_single) { + // The suggestion to `consider` making the destination __single here is omitted + // expected-error-re@+1{{cannot assign to indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'{{$}}}} + global_opaque_array[0] = explicit_single; +} + +void assign_local_opaque_array(void* __single explicit_single) { + void* aloa_array[2]; // Implicitly void* __single aloa_array[2]; + aloa_array[0] = explicit_single; // No diagnostic +} + +//============================================================================== +// No FixIts +// +// The lack of FixIts here is just an implementation limitation. +// rdar://114478465 +//============================================================================== + +typedef struct StructWithExplicitBidiPtrOIAEB { + // expected-note@+2{{pointer 'StructWithExplicitBidiPtrOIAEB::oiaeb_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __bidi_indexable oiaeb_bidi_ptr; + void* __bidi_indexable oiaeb_bidi_ptr; // No fix +} StructWithExplicitBidiPtrOIAEB_t; + +typedef struct StructWithExplicitIdxPtrOIAEB { + // expected-note@+2{{pointer 'StructWithExplicitIdxPtrOIAEB::oiaeb_idx_ptr' declared here}} + // FIXIT-CHECK: void* __indexable oiaeb_idx_ptr; + void* __indexable oiaeb_idx_ptr; // No fix +} StructWithExplicitIdxPtrOIAEB_t; + +int opaque_init_assign_explicit_bidi(F_t* imp_single) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local1' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local1' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oiaeb_local1 = imp_single; + F_t* __bidi_indexable oiaeb_local1 = imp_single; // No fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local2' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local2' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oiaeb_local2 = single_source(); + F_t* __bidi_indexable oiaeb_local2 = single_source(); // No fix + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitBidiPtrOIAEB::oiaeb_bidi_ptr' as '__single'}} + StructWithExplicitBidiPtrOIAEB_t oiaeb_local3 = {.oiaeb_bidi_ptr = imp_single}; + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local4' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local4' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __bidi_indexable oiaeb_local4 = imp_single; + MACRO_PTR_TY __bidi_indexable oiaeb_local4 = imp_single; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local5' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local5' declared here}} + // FIXIT-CHECK: MACRO_TY* __bidi_indexable oiaeb_local5 = imp_single; + MACRO_TY* __bidi_indexable oiaeb_local5 = imp_single; // No Fix +} + +typedef struct StructWithExplicitBidiPtrOAEB { + // expected-note@+2{{pointer 'StructWithExplicitBidiPtrOAEB::oaeb_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __bidi_indexable oaeb_bidi_ptr; + void* __bidi_indexable oaeb_bidi_ptr; // No fix +} StructWithExplicitBidiPtrOAEB_t; + +typedef struct StructWithExplicitIdxPtrOAEI { + // expected-note@+2{{pointer 'StructWithExplicitIdxPtrOAEI::oaei_idx_ptr' declared here}} + // FIXIT-CHECK: void* __indexable oaei_idx_ptr; + void* __indexable oaei_idx_ptr; // No fix +} StructWithExplicitIdxPtrOAEI_t; + +int opaque_assign_explicit_bidi(F_t* imp_single) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaeb_local1' as '__single'}} + // expected-note@+2{{pointer 'oaeb_local1' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oaeb_local1 = 0; + F_t* __bidi_indexable oaeb_local1 = 0; // No Fix + oaeb_local1 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaeb_local2' as '__single'}} + // expected-note@+2{{pointer 'oaeb_local2' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oaeb_local2 = 0; + F_t* __bidi_indexable oaeb_local2 = 0; // No Fix + oaeb_local2 = single_source(); + + StructWithExplicitBidiPtrOAEB_t oaeb_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitBidiPtrOAEB::oaeb_bidi_ptr' as '__single'}} + oaeb_local3.oaeb_bidi_ptr = imp_single; +} + +int opaque_init_assign_explicit_idx(F_t* imp_single) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local1' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local1' declared here}} + // FIXIT-CHECK: F_t* __indexable oiaei_local1 = imp_single; + F_t* __indexable oiaei_local1 = imp_single; // No fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local2' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local2' declared here}} + // FIXIT-CHECK: F_t* __indexable oiaei_local2 = single_source(); + F_t* __indexable oiaei_local2 = single_source(); // No fix + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitIdxPtrOIAEB::oiaeb_idx_ptr' as '__single'}} + StructWithExplicitIdxPtrOIAEB_t oiaei_local3 = {.oiaeb_idx_ptr = imp_single}; + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local4' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local4' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __indexable oiaei_local4 = imp_single; + MACRO_PTR_TY __indexable oiaei_local4 = imp_single; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local5' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local5' declared here}} + // FIXIT-CHECK: MACRO_TY* __indexable oiaei_local5 = imp_single + MACRO_TY* __indexable oiaei_local5 = imp_single; // No Fix +} + +int opaque_assign_explicit_idx(F_t* imp_single) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaei_local1' as '__single'}} + // expected-note@+2{{pointer 'oaei_local1' declared here}} + // FIXIT-CHECK: F_t* __indexable oaei_local1 = 0; + F_t* __indexable oaei_local1 = 0; // No Fix + oaei_local1 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaei_local2' as '__single'}} + // expected-note@+2{{pointer 'oaei_local2' declared here}} + // FIXIT-CHECK: F_t* __indexable oaei_local2 = 0; + F_t* __indexable oaei_local2 = 0; // No Fix + oaei_local2 = single_source(); + + StructWithExplicitIdxPtrOAEI_t oaei_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitIdxPtrOAEI::oaei_idx_ptr' as '__single'}} + oaei_local3.oaei_idx_ptr = imp_single; +} + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_explicit_bidi' as '__single'}} +// expected-note@+2{{pointer 'global_F_explicit_bidi' declared here}} +// FIXIT-CHECK: F_t* __bidi_indexable global_F_explicit_bidi = single_source(); +F_t* __bidi_indexable global_F_explicit_bidi = single_source(); // No Fix + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_explicit_idx' as '__single'}} +// expected-note@+2{{pointer 'global_F_explicit_idx' declared here}} +// FIXIT-CHECK: F_t* __indexable global_F_explicit_idx = single_source(); +F_t* __indexable global_F_explicit_idx = single_source(); // No Fix + +//============================================================================== +// No FixIts +//============================================================================== + +// This is cast so there's no destination variable to modify at the point the +// diagnostic is emitted. +F_t* foo3(F_t* imp_single) { + // expected-error-re@+2{{cannot cast from __single pointer to incomplete type {{.+}} to indexable pointer type {{.+}}}} + // FIXIT-CHECK: return (F_t* __bidi_indexable) imp_single; + return (F_t* __bidi_indexable) imp_single; // No Fix +} diff --git a/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c b/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c new file mode 100644 index 0000000000000..a324793558508 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c @@ -0,0 +1,931 @@ +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include +#include + +int global; + +void cb(int *__counted_by(count) p, int count); +void sb(void *__sized_by(size) p, int size); +void cb_multi(int *__counted_by(c1 - c2) p, int c1, int c2); +void cb_out(int *__counted_by(*count) p, int *count); +void cb_or_null(int *__counted_by_or_null(count) p, int count); +void sb_or_null(void *__sized_by_or_null(size) p, int size); + +// Check if the pointer argument has an explicit attribute. + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void explicit_parm(int *__single p1, int l1, int *__counted_by(l2) p2, int l2) { + cb(p1, l1); + cb(p2, l2); + + int k1 = l1; + int *__counted_by(k1) q1 = p1; + + int k2 = l2; + int *__counted_by(k1) q2 = p2; + + q1 = p1; + k1 = l1; + + q2 = p2; + k2 = l2; +} + +void explicit_struct(void) { + struct s { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *__single p1; + int l1; + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *__counted_by(l2) p2; + int l2; + } s; + cb(s.p1, s.l1); + cb(s.p2, s.l2); + + int k1 = s.l1; + int *__counted_by(k1) q1 = s.p1; + + int k2 = s.l2; + int *__counted_by(k1) q2 = s.p2; + + q1 = s.p1; + k1 = s.l1; + + q2 = s.p2; + k2 = s.l2; +} + +// Check if the pointer types are compatible. + +void cb_i8(int8_t *__counted_by(len) p, int len); +void sb_i8(int8_t *__sized_by(size) p, int size); +void cb_or_null_i8(int8_t *__counted_by_or_null(len) p, int len); +void sb_or_null_i8(int8_t *__sized_by_or_null(size) p, int size); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(len) " +void cb_types_ok_passing(int8_t *p, int len) { + cb_i8(p, len); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__counted_by(len) " +void cb_types_ok_init(int8_t *p, int len) { + int l = len; + int8_t *__counted_by(l) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by(len) " +void cb_types_ok_assign(int8_t *p, int len) { + int l; + int8_t *__counted_by(l) q; + q = p; + l = len; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void cb_types_mismatch(int16_t *p, int len) { + cb_i8(p, len); + + int l = len; + int8_t *__counted_by(l) q = p; + + q = p; + l = len; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__sized_by(size) " +void sb_types_ok_passing(int8_t *p, int size) { + sb_i8(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__sized_by(size) " +void sb_types_ok_init(int8_t *p, int size) { + int s = size; + int8_t *__sized_by(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by(size) " +void sb_types_ok_assign(int8_t *p, int size) { + int s; + int8_t *__sized_by(s) q; + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:36-[[@LINE+1]]:36}:"__sized_by(size) " +void sb_types_ok2_passing(int16_t *p, int size) { + sb_i8(p, size); // size is in bytes, pointee size does not matter. +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by(size) " +void sb_types_ok2_init(int16_t *p, int size) { + int s = size; + int8_t *__sized_by(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__sized_by(size) " +void sb_types_ok2_assign(int16_t *p, int size) { + int s; + int8_t *__sized_by(s) q; + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:42-[[@LINE+1]]:42}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_passing(int8_t *p, int len) { + cb_or_null_i8(p, len); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:39-[[@LINE+1]]:39}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_init(int8_t *p, int len) { + int l = len; + int8_t *__counted_by_or_null(l) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_assign(int8_t *p, int len) { + int l; + int8_t *__counted_by_or_null(l) q; + q = p; + l = len; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void cb_or_null_types_mismatch(int16_t *p, int len) { + cb_or_null_i8(p, len); + + int l = len; + int8_t *__counted_by_or_null(l) q = p; + + q = p; + l = len; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:42-[[@LINE+1]]:42}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_passing(int8_t *p, int size) { + sb_or_null_i8(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:39-[[@LINE+1]]:39}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_init(int8_t *p, int size) { + int s = size; + int8_t *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_assign(int8_t *p, int size) { + int s; + int8_t *__sized_by_or_null(s) q; + + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:44-[[@LINE+1]]:44}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_passing(int16_t *p, int size) { + sb_or_null_i8(p, size); // size is in bytes, pointee size does not matter. +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_init(int16_t *p, int size) { + int s = size; + int8_t *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:43-[[@LINE+1]]:43}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_assign(int16_t *p, int size) { + int s; + int8_t *__sized_by_or_null(s) q; + + q = p; + s = size; +} + +// Check syntax of count expression. + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_passing(int *p, int len) { + cb(p, len + 2 * (len + 1) - 42); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:26-[[@LINE+1]]:26}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_init(int *p, int len) { + int l = len + 2 * (len + 1) - 42; + int *__counted_by(l) q = p; +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:28-[[@LINE+1]]:28}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_assign(int *p, int len) { + int l; + int *__counted_by(l) q; + + q = p; + l = len + 2 * (len + 1) - 42; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void syntax_bad(int *p, int len) { + cb(p, len == 42 ? 42 : len + 1); // ?: operator is unsupported. + + int l = len == 42 ? 42 : len + 1; + int *__counted_by(l) q = p; + + q = p; + l = len == 42 ? 42 : len + 1; +} + +// Check if the decls in the count argument are parameters of the same function as the pointer argument. + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void decls_bad(int *p, int len) { + int local; + cb(p, local); + cb(p, global); + cb(p, len + local + global); + cb_multi(p, len, local); + cb_multi(p, global, len); + + int l1 = local; + int *__counted_by(l1) q2 = p; + q2 = p; + l1 = local; + + int l2 = global; + int *__counted_by(l2) q2 = p; + q2 = p; + l2 = global; + + int l3 = len + local + global; + int *__counted_by(l3) q3 = p; + q3 = p; + l3 = len + local + global; + + int l4_a = len; + int l4_b = local; + int *__counted_by(l4 - l4_b) q4 = p; + q4 = p; + len4_a = len; + len4_b = local; + + int l5_a = global; + int l5_b = len; + int *__counted_by(l5_a - l5_b) q5 = p; + q5 = p; + l5_a = global; + l5_b = len; +} + +// Check if the decls in the count argument are parameters of the same function as the pointer argument. + +void member_base_ok_passing(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + cb(f.p, f.len); +} + +void member_base_ok_init(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + int l = f.len; + int *__counted_by(l) q = f.p; +} + +void member_base_ok_assign(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + int l; + int *__counted_by(l) q; + q = f.p; + l = f.len; +} + +void member_base_ok_ptr_passing(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + cb(f->p, f->len); +} + +void member_base_ok_ptr_init(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + int l = f->len; + int *__counted_by(l) q = f->p; +} + +void member_base_ok_ptr_assign(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + int l; + int *__counted_by(l) q; + q = f->p; + l = f->len; +} + +void member_base_ok_multi_passing(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + cb_multi(f.p, f.a + f.b, f.b - 42); +} + +void member_base_ok_multi_init(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + int l1 = f.a + f.b; + int l2 = f.b - 42; + int *__counted_by(l1 - l2) q = f.p; +} + +void member_base_ok_multi_assign(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + int l1; + int l2; + int *__counted_by(l1 - l2) q; + q = f.p; + l1 = f.a + f.b; + l2 = f.b - 42; +} + +void member_base_ok_nested_passing(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + cb(f.b.p, f.b.len); +} + +void member_base_ok_nested_init(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + int l = f.b.len; + int *__counted_by(l) q = f.b.p; +} + +void member_base_ok_nested_assign(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + int l; + int *__counted_by(l) q; + q = f.b.p; + l = f.b.len; +} + +void member_base_ok_ptr_nested_passing(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + cb(f->b->p, f->b->len); +} + +void member_base_ok_ptr_nested_init(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + int l = f->b->len; + int *__counted_by(l) q = f->b->p; +} + +void member_base_ok_ptr_nested_assign(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + int l; + int *__counted_by(l) q; + q = f->b->p; + l = f->b->len; +} + +struct member_base_foo_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_passing(struct member_base_foo_passing *f) { + cb(f->p, f->len); +} + +struct member_base_foo_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_init(struct member_base_foo_init *f) { + int l = f->len; + int *__counted_by(l) q = f->p; +} + +struct member_base_foo_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_assign(struct member_base_foo_assign *f) { + int l; + int *__counted_by(l) q; + q = f->p; + l = f->len; +} + +struct member_base_foo2_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_passing(struct member_base_foo2_passing f) { + cb(f.p, f.len); +} + +struct member_base_foo2_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_init(struct member_base_foo2_init f) { + int l = f.len; + int *__counted_by(l) q = f.p; +} + +struct member_base_foo2_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_assign(struct member_base_foo2_assign f) { + int l; + int *__counted_by(l) q; + q = f.p; + l = f.len; +} + +void member_base_bad(int parm) { + int local; + struct foo { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *p; + int len; + } f, f2; + cb(f.p, local); + cb(f.p, parm); + cb(f.p, global); + cb(f.p, f2.len); + cb_multi(f.p, f.len, f2.len); + cb_multi(f.p, f2.len, f.len); + cb_multi(f.p, f.len, local); + cb_multi(f.p, f.len, parm); + cb_multi(f.p, global, f.len); + + int l1 = local; + int *__counted_by(l1) q1 = f.p; + l1 = parm; + q1 = f.p; + + int l2 = global; + int *__counted_by(l2) q2 = f.p; + l2 = f2.len; + q2 = f.p; + + int l3_a = f.len; + int l3_b = f2.len; + int *__counted_by(l3_a - l3_b) q3 = f.p; + q3 = f.p; + l3_a = f.len; + l3_b = parm; +} + +void member_base_bad2(int parm) { + int local; + struct foo { + struct bar { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *p; + int len; + } b; + int size; + } f, f2; + cb(f.b.p, f.size); + cb(f.b.p, f2.b.len); + cb(f.b.p, f2.size); + cb(f.b.p, f.b.len + f2.b.len); + cb(f.b.p, f.b.len + f.size); + cb(f.b.p, f.b.len + f2.size); + cb(f.b.p, f.b.len + local); + cb(f.b.p, f.b.len + parm); + cb(f.b.p, f.b.len + global); + cb_multi(f.b.p, f.b.len, f2.b.len); + cb_multi(f.b.p, f.b.len, f.size); + + int l1 = f.size; + int *__counted_by(l1) q1 = f.b.p; + l1 = f2.b.len; + q1 = f.b.p; + + int l2 = f.b.len + f2.b.len; + int *__counted_by(l2) q2 = f.b.p; + l2 = f.b.len + parm; + q2 = f.b.p; + + int l3_a = f.b.len; + int l3_b = f2.b.len; + int *__counted_by(l3_a - l3_b) q3 = f.b.p; + q3 = f.b.p; + l3_a = f.b.len; + l3_b = f.size; +} + +struct member_base_global_foo_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_passing(void) { + struct member_base_global_foo_passing f; + cb(f.p, f.len); +} + +struct member_base_global_foo_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_init(void) { + struct member_base_global_foo_init f; + int l = f.len; + int *__counted_by(l) q = f.p; +} + +struct member_base_global_foo_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_assign(void) { + struct member_base_global_foo_assign f; + int l; + int *__counted_by(l) q; + l = f.len; + q = f.p; +} + +// Check if parentheses are emitted for complex expressions, so that we can +// avoid dealing with operator precedence. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:18-[[@LINE+1]]:18}:"__counted_by(len) " +void parens(int *p, int len) { + cb(p, len); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len + len2) " +void parens2(int *p, int len, int len2) { + cb(p, len + len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len - len2) " +void parens3(int *p, int len, int len2) { + cb_multi(p, len, len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len - (len + len2)) " +void parens4(int *p, int len, int len2) { + cb_multi(p, len, len + len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by((len - 42) - (len + len2)) " +void parens5(int *p, int len, int len2) { + cb_multi(p, len - 42, len + len2); +} + +// Check out counts. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__counted_by(*count) " +void out_to_in_passing(int *p, int *count) { + cb(p, *count); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:26-[[@LINE+1]]:26}:"__counted_by(*count) " +void out_to_in_init(int *p, int *count) { + int c = *count; + int *__counted_by(c) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:28-[[@LINE+1]]:28}:"__counted_by(*count) " +void out_to_in_assign(int *p, int *count) { + int c; + int *__counted_by(c) q; + q = p; + c = *count; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:22-[[@LINE+1]]:22}:"__counted_by(*count) " +void out_to_out(int *p, int *count) { + cb_out(p, count); +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void out_to_out_with_arith(int *p, int *count) { + cb_out(p, count + 1); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:21-[[@LINE+1]]:21}:"__counted_by(count) " +void in_to_out(int *p, int count) { + cb_out(p, &count); +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void in_to_out_with_arith(int *p, int count) { + cb_out(p, &count + 1); +} + +// Check *_or_null() variant. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by_or_null(count) " +void to_cb_or_null_passing(int *p, int count) { + cb_or_null(p, count); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__counted_by_or_null(count) " +void to_cb_or_null_init(int *p, int count) { + int c = count; + int *__counted_by_or_null(c) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:32-[[@LINE+1]]:32}:"__counted_by_or_null(count) " +void to_cb_or_null_assign(int *p, int count) { + int c; + int *__counted_by_or_null(c) q; + q = p; + c = count; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by_or_null(size) " +void to_sb_or_null_passing(int *p, int size) { + sb_or_null(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__sized_by_or_null(size) " +void to_sb_or_null_init(int *p, int size) { + int s = size; + int *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:32-[[@LINE+1]]:32}:"__sized_by_or_null(size) " +void to_sb_or_null_assign(int *p, int size) { + int s; + int *__sized_by_or_null(s) q; + q = p; + s = size; +} + +// Check constant counts. + +void cb_const(int *__counted_by(16) p); +void sb_const(void *__sized_by(16) p); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:36-[[@LINE+1]]:36}:"__counted_by(16) " +void to_cb_const_parm_passing(int *p) { + cb_const(p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by(16) " +void to_cb_const_parm_init(int *p) { + int *__counted_by(16) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__counted_by(16) " +void to_cb_const_parm_assign(int *p) { + int *__counted_by(16) q; + q = p; +} + +void to_cb_const_struct(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } f; + cb_const(f.p); +} + +void to_cb_const_struct_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } *f; + cb_const(f->p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__sized_by(16) " +void to_sb_const_parm(void *p) { + sb_const(p); +} + +void to_sb_const_struct(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } f; + sb_const(f.p); +} + +void to_sb_const_struct_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } *f; + sb_const(f->p); +} + +// Check macros. + +// TODO: We should suggest __counted_by(COUNT)/__sized_by(COUNT) instead. +// rdar://119737647 + +#define COUNT 16 + +void cb_const_macro(int *__counted_by(COUNT) p); +void sb_const_macro(void *__sized_by(COUNT) p); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(16) " +void to_cb_const_parm_macro(int *p) { + cb_const_macro(p); +} + +void to_cb_const_struct_macro(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } f; + cb_const_macro(f.p); +} + +void to_cb_const_struct_macro_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } *f; + cb_const_macro(f->p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__sized_by(16) " +void to_sb_const_parm_macro(void *p) { + sb_const_macro(p); +} + +void to_sb_const_struct_macro(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } f; + sb_const_macro(f.p); +} + +void to_sb_const_struct_macro_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } *f; + sb_const_macro(f->p); +} + +// Check init patterns. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__counted_by(len) " +void init_struct(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo f = { p, len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:24-[[@LINE+1]]:24}:"__counted_by(len) " +void init_struct2(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + } f = { p, len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(len) " +void init_struct_designated(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo f = { .q = p, .l = len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__counted_by(len) " +void init_struct_designated2(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + } f = { .q = p, .l = len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__counted_by(len) " +void init_struct_nested(int *p, int len) { + struct bar { + struct foo { + int *__counted_by(l) q; + int l; + } f; + int x; + }; + struct bar b = { { p, len }, 0 }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__counted_by(len) " +void init_struct_nested_designated(int *p, int len) { + struct bar { + struct foo { + int *__counted_by(l) q; + int l; + } f; + int x; + }; + struct bar b = { .x = 0, .f = { .l = len, .q = p} }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:22-[[@LINE+1]]:22}:"__counted_by(len) " +void init_array(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo array[2] = { { p, len } }; +} diff --git a/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c b/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c new file mode 100644 index 0000000000000..4c909fe8284ce --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c @@ -0,0 +1,94 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck %s --input-file=%t.cc_out + +#include + +// expected-note@+1 6{{passing argument to parameter 'path' here}} +void method_with_single_const_param(const char *path); + +// expected-note@+1 5{{passing argument to parameter 'path' here}} +void method_with_single_null_terminated_param(char * __null_terminated path); + +struct Foo { +// expected-note@+4 {{consider adding '__null_terminated' to 'buf'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+3]]:9-[[@LINE+3]]:9}:"__null_terminated " +// expected-note@+2{{consider adding 'const' to 'buf'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *buf; + +// expected-note@+4 {{consider adding '__null_terminated' to 'arr_in_struct'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+3]]:9-[[@LINE+3]]:9}:"__null_terminated " +// expected-note@+2{{consider adding 'const' to 'arr_in_struct'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *arr_in_struct[4]; +}; + +void test_invocation_with_struct_fields(struct Foo f) { + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(f.arr_in_struct[0]); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(f.arr_in_struct[0]); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(f.buf); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(f.buf); +} + +// expected-note@+2{{consider adding 'const' to 'anchor'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:25-[[@LINE+1]]:25}:"const " +void single_invocation1(char *anchor) +{ + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(anchor); +} + +// expected-note@+2{{consider adding '__null_terminated' to 'anchor'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__null_terminated " +void single_invocation2(char *anchor) +{ + //expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(anchor); +} + +void test_single_invocation_local_variable() { + // expected-note@+2 {{consider adding 'const' to 'local_arr'}} + // CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *local_arr[2]; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(local_arr[0]); +} + +void test_single_invocation2_local_variable() { + // expected-note@+2 {{consider adding '__null_terminated' to 'local_arr'}} + // CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:9-[[@LINE+1]]:9}:"__null_terminated " + char *local_arr[2]; + + //expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(local_arr[0]); +} + +void test_single_invocation4_local_variable() { + char * __single local_arr[2]; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(local_arr[0]); +} + +// TODO: Emit a note and a fixit here. rdar://122997544 +char * returns_single(void); + +void test_invocation_with_return_values() { + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(returns_single()); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(returns_single()); +} diff --git a/clang/test/BoundsSafety/Frontend/asm_is_supported.S b/clang/test/BoundsSafety/Frontend/asm_is_supported.S new file mode 100644 index 0000000000000..b657829a23a9d --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/asm_is_supported.S @@ -0,0 +1,5 @@ + + +// Making sure the Frontend accepts the -fbounds-safety flags in assembly mode - it would otherwise produce error: unknown argument '...' + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety %s diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c b/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c new file mode 100644 index 0000000000000..098db97f033df --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c @@ -0,0 +1,7 @@ + +// RUN: %clang_cc1 -fbounds-safety-adoption-mode -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety-adoption-mode -fbounds-safety -fno-bounds-safety -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety -fbounds-safety-adoption-mode -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-adoption-mode -fno-bounds-safety -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-safety-adoption-mode without -fbounds-safety is ignored diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c b/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c new file mode 100644 index 0000000000000..6c3b5e6cdce36 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x c++ -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x objective-c -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x objective-c++ -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: not %clang_cc1 -x c++ -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// RUN: not %clang_cc1 -x objective-c -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// RUN: not %clang_cc1 -x objective-c++ -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language + + +// RUN: not %clang_cc1 -fbounds-safety -fno-experimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-DIS %s + +// CHECK-DIS: error: -fexperimental-bounds-safety-attributes cannot be disabled when -fbounds-safety is enabled diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c b/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c new file mode 100644 index 0000000000000..8e7fe1c05a88f --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c @@ -0,0 +1,21 @@ + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=none -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_NONE +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=none,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_NONE + +// POS_NONE: invalid value 'none' in '-fbounds-safety-bringup-missing-checks='; did you mean '-fno-bounds-safety-bringup-missing-checks'? + +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=none -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_NONE +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=none,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_NONE + +// NEG_NONE: invalid value 'none' in '-fno-bounds-safety-bringup-missing-checks='; did you mean '-fbounds-safety-bringup-missing-checks'? + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=abc -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_ABC +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=abc,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_ABC + +// POS_ABC: invalid value 'abc' in '-fbounds-safety-bringup-missing-checks=' + + +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=abc -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_ABC +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=abc,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_ABC + +// NEG_ABC: invalid value 'abc' in '-fno-bounds-safety-bringup-missing-checks=' diff --git a/clang/test/BoundsSafety/Frontend/cmdl_opts.c b/clang/test/BoundsSafety/Frontend/cmdl_opts.c new file mode 100644 index 0000000000000..dc5de416eb762 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cmdl_opts.c @@ -0,0 +1,9 @@ + + +// Making sure the Frontend accepts the -fbounds-safety flags - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -cc1 -fbounds-safety -fsyntax-only %s + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp b/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp new file mode 100644 index 0000000000000..d967eb95d433c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp @@ -0,0 +1,3 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp b/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp new file mode 100644 index 0000000000000..7e61a5fc25bbe --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp @@ -0,0 +1,5 @@ + + +// RUN: %clang_cc1 -fbounds-attributes-cxx-experimental -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-attributes-cxx-experimental without -fbounds-attributes is ignored diff --git a/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp b/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c b/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c new file mode 100644 index 0000000000000..a274c0380f053 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -x c -verify=c_lang %s +// RUN: %clang_cc1 -x c++ -verify=cpp_lang %s + +void Test1() { + __builtin_unsafe_forge_bidi_indexable(0, sizeof(int)); + // c_lang-error@-1 {{use of unknown builtin '__builtin_unsafe_forge_bidi_indexable'}} + // cpp_lang-error@-2 {{use of undeclared identifier '__builtin_unsafe_forge_bidi_indexable'}} +} diff --git a/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m b/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m new file mode 100644 index 0000000000000..fb9d1d7e18197 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m @@ -0,0 +1,2 @@ + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp b/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp new file mode 100644 index 0000000000000..1d1694bf25c6c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp @@ -0,0 +1,5 @@ + + +// RUN: %clang -cc1 -fbounds-attributes-objc-experimental -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-attributes-objc-experimental without -fbounds-attributes is ignored diff --git a/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m b/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm b/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/only_c_is_supported.c b/clang/test/BoundsSafety/Frontend/only_c_is_supported.c new file mode 100644 index 0000000000000..b98ccd2fd9ab7 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/only_c_is_supported.c @@ -0,0 +1,10 @@ + +// RUN: not %clang -cc1 -fbounds-safety -x c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c new file mode 100644 index 0000000000000..1cba3e33ea25f --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c @@ -0,0 +1,81 @@ + +// RUN: %clang_cc1 -dump-tokens %s 2>&1 | FileCheck %s + +#include + +int HasPtrCheck = __has_ptrcheck; +// CHECK-LABEL: identifier 'HasPtrCheck' +// CHECK: numeric_constant '0' + +struct Foo { +// CHECK-LABEL: identifier 'Foo' +// CHECK-NEXT: l_brace + int *__single f0; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f0' +// CHECK-LABEL: semi + + int *__unsafe_indexable f1; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f1' +// CHECK-LABEL: semi + + int *__counted_by(10) f2; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f2' +// CHECK-LABEL: semi + + int *__sized_by(10) f3; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f3' +// CHECK-LABEL: semi +}; +// CHECK-LABEL: r_brace +// CHECK: semi + +__ptrcheck_abi_assume_single(); +// CHECK-NEXT: semi +__ptrcheck_abi_assume_unsafe_indexable(); +// CHECK-NEXT: semi + +void code(void) { +// CHECK-LABEL: l_brace + __unsafe_forge_single(int *, 100); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '100' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren + +// CHECK-LABEL: semi + __unsafe_forge_bidi_indexable(int *, 200, 300); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '200' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren + +// CHECK-LABEL: semi + __unsafe_forge_terminated_by(int *, 200, 300); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '200' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c new file mode 100644 index 0000000000000..c5b5964ac5eb8 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c @@ -0,0 +1,4 @@ + +// RUN: %clang -fsyntax-only %s + +#include diff --git a/clang/test/BoundsSafety/Lexer/bidi_indexable.c b/clang/test/BoundsSafety/Lexer/bidi_indexable.c new file mode 100644 index 0000000000000..52c43c9df4f5f --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/bidi_indexable.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((bidi_indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'bidi_indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' diff --git a/clang/test/BoundsSafety/Lexer/ended_by.c b/clang/test/BoundsSafety/Lexer/ended_by.c new file mode 100644 index 0000000000000..a83f9c9db2290 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/ended_by.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((ended_by(end))) start; + int *end; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'ended_by' + // CHECK: l_paren '(' + // CHECK: identifier 'end' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'start' + // CHECK: semi ';' + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: identifier 'end' + // CHECK: semi '; diff --git a/clang/test/BoundsSafety/Lexer/indexable.c b/clang/test/BoundsSafety/Lexer/indexable.c new file mode 100644 index 0000000000000..f7d18d660d40a --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/indexable.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' +}; diff --git a/clang/test/BoundsSafety/Lexer/single.c b/clang/test/BoundsSafety/Lexer/single.c new file mode 100644 index 0000000000000..efb2ce05ce305 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/single.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((single)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'single' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c new file mode 100644 index 0000000000000..38a1c71181a99 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c @@ -0,0 +1,7 @@ + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_bidi_indexable(0, sizeof(int)); + // CHECK: __builtin_unsafe_forge_bidi_indexable '__builtin_unsafe_forge_bidi_indexable' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c new file mode 100644 index 0000000000000..e09c85bc2ac28 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c @@ -0,0 +1,8 @@ + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_single(0); + // CHECK: __builtin_unsafe_forge_single '__builtin_unsafe_forge_single' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c new file mode 100644 index 0000000000000..9517a5aeec1eb --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_terminated_by(int*, 1, 2); + // CHECK: __builtin_unsafe_forge_terminated_by '__builtin_unsafe_forge_terminated_by' + // CHECK: l_paren '(' + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: comma ',' + // CHECK: numeric_constant '1' + // CHECK: comma ',' + // CHECK: numeric_constant '2' + // CHECK: r_paren ')' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_indexable.c b/clang/test/BoundsSafety/Lexer/unsafe_indexable.c new file mode 100644 index 0000000000000..1153aa328d566 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_indexable.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((unsafe_indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'unsafe_indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' +} diff --git a/clang/test/BoundsSafety/Macros/c99-variadic-macros.c b/clang/test/BoundsSafety/Macros/c99-variadic-macros.c new file mode 100644 index 0000000000000..5b2dee47250b1 --- /dev/null +++ b/clang/test/BoundsSafety/Macros/c99-variadic-macros.c @@ -0,0 +1,22 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s + +// expected-no-diagnostics + +#include + +void foo(void) { + char *p; + + // This macro cannot be declared as __unsafe_null_terminated_from_indexable(P, ...), + // because then the C standard pre-C23 doesn't allow passing only 1 argument. + __unsafe_null_terminated_from_indexable((void)p); + __unsafe_null_terminated_from_indexable((void)p, trailing); + __unsafe_null_terminated_from_indexable((void)p, trailing, trailing2); + + // This macro cannot be declared as __unsafe_terminated_by_from_indexable(T, P, ...), + // because then the C standard pre-C23 doesn't allow passing only 2 arguments. + __unsafe_terminated_by_from_indexable(filler, (void)p); + __unsafe_terminated_by_from_indexable(filler, (void)p, trailing); + __unsafe_terminated_by_from_indexable(filler, (void)p, trailing, trailing2); +} diff --git a/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h b/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h new file mode 100644 index 0000000000000..1abf3300d76ee --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h @@ -0,0 +1,17 @@ +#include + +#ifndef COUNT_ASSIGN_MODULE_H +#define COUNT_ASSIGN_MODULE_H + +int foo(int *__counted_by(len) ptr, int len) { + int arr[10]; + ptr = arr; + len = 10; + return ptr[len-1]; +} + +void baz() { + int len; + void *__sized_by(len) ptr; +} +#endif \ No newline at end of file diff --git a/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap b/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap new file mode 100644 index 0000000000000..95f029e3c487c --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap @@ -0,0 +1,2 @@ +module ca { header "count-assign-module.h" export * } + diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h new file mode 100644 index 0000000000000..fded5f0c9281d --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h @@ -0,0 +1,5 @@ +#if __has_include() +#include +#else +#define __has_ptrcheck 0 +#endif diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap new file mode 100644 index 0000000000000..718ea590ce54a --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap @@ -0,0 +1,9 @@ +module cdefs [system] [no_undeclared_includes] { + header "cdefs.h" + export * +} + +module ptrcheck { + header "ptrcheck.h" + export * +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h new file mode 100644 index 0000000000000..b8b85a9dd5ee1 --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h @@ -0,0 +1 @@ +#define __has_ptrcheck 1 diff --git a/clang/test/BoundsSafety/Modules/clang-headers.c b/clang/test/BoundsSafety/Modules/clang-headers.c new file mode 100644 index 0000000000000..964c7e778ee12 --- /dev/null +++ b/clang/test/BoundsSafety/Modules/clang-headers.c @@ -0,0 +1,7 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t/module_cache -Rmodule-import -verify %s +// RUN: %clang_cc1 -fsyntax-only -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t/module_cache -Rmodule-import -verify %s + +#include // expected-remark-re{{importing module 'ptrcheck' from '{{.*}}.pcm'}} diff --git a/clang/test/BoundsSafety/Modules/count-assign.c b/clang/test/BoundsSafety/Modules/count-assign.c new file mode 100644 index 0000000000000..5be680d4ea02c --- /dev/null +++ b/clang/test/BoundsSafety/Modules/count-assign.c @@ -0,0 +1,27 @@ +// REQUIRES: system-darwin +// RUN: rm -rf %t +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -emit-module %S/Inputs/count-assign/module.modulemap -fmodule-name=ca -o %t/count-assign.pcm +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -ast-dump-all -o - %s -fmodule-file=%t/count-assign.pcm | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -emit-llvm -o - %s -fmodule-file=%t/count-assign.pcm | FileCheck %s -check-prefix=LLVM +#include "count-assign-module.h" + +int bar(void) { + int arr[10]; + return foo(arr, 10); +} + +// CHECK: |-FunctionDecl {{.*}} imported in ca used foo 'int (int *__single __counted_by(len), int)' +// CHECK: | |-ParmVarDecl [[PARM_DECL_PTR:0x[a-z0-9]*]] {{.*}} imported in ca used ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} imported in ca used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[PARM_DECL_PTR]] 0 + +// CHECK: |-FunctionDecl {{.*}} imported in ca baz 'void ()' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.*}} imported in ca used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[LOCAL_PTR_REF:0x[a-z0-9]*]] 0 +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[LOCAL_PTR_REF]] {{.*}} imported in ca ptr 'void *__single __sized_by(len)':'void *__single' + +// Check that we do not accidentally set nullcheck on expr loaded from pcm. +// LLVM-NOT: boundscheck.null diff --git a/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m b/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m new file mode 100644 index 0000000000000..2f320d29a222e --- /dev/null +++ b/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/ptrcheck-include-from-darwin/ %s -verify +// expected-no-diagnostics + +#include "cdefs.h" + +#if !__has_ptrcheck + #error __has_ptrcheck is not defined to 1 +#endif diff --git a/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c b/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c new file mode 100644 index 0000000000000..bee80cc9e421e --- /dev/null +++ b/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c @@ -0,0 +1,20 @@ +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t -fsyntax-only -verify %s + +#ifndef HEADER +#define HEADER +int Test(int *singleArg) { + int *local = singleArg; + + return *local; +} +#else + +float Test(float); // expected-error{{conflicting types for 'Test'}} + // expected-note@-8{{previous definition is here}} + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-assign-with-pch.c b/clang/test/BoundsSafety/PCH/count-assign-with-pch.c new file mode 100644 index 0000000000000..439aef0bc245a --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-assign-with-pch.c @@ -0,0 +1,30 @@ +// REQUIRES: system-darwin +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t %s -ast-dump-all 2>&1 | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +int foo(int *__counted_by(len) ptr, int len) { + ptr = ptr; + len = 10; + return ptr[len-1]; +} +#else + +int main() { + int arr[10] = {0}; + return foo(arr, 10); +} + +// CHECK: |-FunctionDecl {{.*}} imported used foo 'int (int *__single __counted_by(len), int)' +// CHECK: | |-ParmVarDecl [[PTR_DECL:0x[a-z0-9]*]] {{.*}} imported used ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} imported used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[PTR_DECL]] 0 + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c b/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c new file mode 100644 index 0000000000000..b25667026f2d1 --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c @@ -0,0 +1,38 @@ +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t -verify -ast-dump-all %s 2>&1 | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +typedef struct { + int len; + int *__counted_by(len) ptr; +} S; +int foo() { + int arr[10]; + S s; + s.len = 10; + s.ptr = arr; + return s.ptr[9]; +} +#else + +int main() { + return foo(); +} + +// CHECK: |-RecordDecl [[STRUCT_DEF:0x[a-z0-9]*]] {{.*}} imported struct definition +// CHECK: | |-FieldDecl {{.*}} imported referenced len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_DECL_PTR:0x[a-z0-9]*]] 0 +// CHECK: | `-FieldDecl [[FIELD_DECL_PTR]] {{.*}} imported referenced ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: |-TypedefDecl {{.*}} imported referenced S 'struct S':'S' +// CHECK: | `-ElaboratedType {{.*}} 'struct S' sugar imported +// CHECK: | `-RecordType {{.*}} 'S' imported +// CHECK: | `-Record [[STRUCT_DEF]] + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-increment-with-pch.c b/clang/test/BoundsSafety/PCH/count-increment-with-pch.c new file mode 100644 index 0000000000000..6500e224c76cb --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-increment-with-pch.c @@ -0,0 +1,100 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t -verify -emit-llvm -O0 %s -o - | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +typedef struct { + int len; + int *__counted_by(len) ptr; +} S; +int foo() { + int arr[10]; + S s = {0}; + s.ptr = arr; + s.len++; + return 0; +} +#else + +int main() { + return foo(); +} + +#endif + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align {{[0-9]+}} +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align {{[0-9]+}} +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align {{[0-9]+}} +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align {{[0-9]+}} +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align {{[0-9]+}} +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align {{[0-9]+}} +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation !{{[0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align {{[0-9]+}} +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align {{[0-9]+}} +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP10]], align {{[0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP11]], align {{[0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP12]], align {{[0-9]+}} +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[LEN1]], align {{[0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP13]], 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align {{[0-9]+}} [[AGG_TEMP2]], ptr align {{[0-9]+}} [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align {{[0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align {{[0-9]+}} [[AGG_TEMP3]], ptr align {{[0-9]+}} [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align {{[0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align {{[0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align {{[0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP6:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br i1 [[CMP6]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sle i32 0, [[ADD]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP18:%.*]] = phi i1 [ false, [[CONT]] ], [ [[CMP7]], [[LAND_RHS]] ], !annotation !{{[0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT9:%.*]], label [[TRAP8:%.*]], !annotation !{{[0-9]+}} +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont10: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align {{[0-9]+}} [[AGG_TEMP10]], ptr align {{[0-9]+}} [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align {{[0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align {{[0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align {{[0-9]+}} +// CHECK-NEXT: [[PTR17:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR12]], ptr [[PTR17]], align {{[0-9]+}} +// CHECK-NEXT: [[TMP21:%.*]] = load i32, ptr [[LEN1]], align {{[0-9]+}} +// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP21]], 1 +// CHECK-NEXT: store i32 [[INC]], ptr [[LEN1]], align {{[0-9]+}} +// CHECK-NEXT: ret i32 0 +// diff --git a/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c b/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c new file mode 100644 index 0000000000000..d2cab464c8bf3 --- /dev/null +++ b/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o - | FileCheck %s + +#include + +struct s { + int count; + int fam[__counted_by(count)]; +}; + +void bar(struct s *p); + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[BUF:%.*]], i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_foo, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PGOCOUNT]], 1 +// CHECK-NEXT: store i64 [[TMP0]], ptr @__profc_foo, align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[BUF]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label [[CONT40:%.*]], label [[FLEX_BASE_NONNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[BUF]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[BUF]], i64 4 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[TMP1]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont27: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[BUF]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sgt i32 [[TMP2]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT30:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[IDX_EXT]], -4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[TMP2]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp ult i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT50:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND52:%.*]] = or i1 [[DOTNOT50]], [[FLEX_COUNT_CHECK_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND52]], label [[TRAP]], label [[CONT40]], {{!annotation ![0-9]+}} +// CHECK: cont40: +// CHECK-NEXT: tail call void @bar(ptr noundef [[BUF]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: ret void +// +void foo(void *__sized_by(size) buf, unsigned size) { + struct s *p = (struct s *)buf; + bar(p); +} diff --git a/clang/test/BoundsSafety/Profile/region-counter-map.c b/clang/test/BoundsSafety/Profile/region-counter-map.c new file mode 100644 index 0000000000000..783f8a18aeb93 --- /dev/null +++ b/clang/test/BoundsSafety/Profile/region-counter-map.c @@ -0,0 +1,169 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-attributes -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o - | FileCheck %s + +#include + +void *__sized_by(len) foo(void *__sized_by(len) buf, unsigned long long len); + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store i64 [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_bar, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PGOCOUNT]], 1 +// CHECK-NEXT: store i64 [[TMP0]], ptr @__profc_bar, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP2]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP11:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP11]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: [[PGOCOUNT69:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 3), align 8 +// CHECK-NEXT: [[TMP8:%.*]] = add i64 [[PGOCOUNT69]], 1 +// CHECK-NEXT: store i64 [[TMP8]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 3), align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LOP_RHSCNT:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: lop.rhscnt: +// CHECK-NEXT: [[PGOCOUNT70:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 4), align 8 +// CHECK-NEXT: [[TMP10:%.*]] = add i64 [[PGOCOUNT70]], 1 +// CHECK-NEXT: store i64 [[TMP10]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 4), align 8 +// CHECK-NEXT: br label [[LAND_RHS:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[PGOCOUNT71:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 1), align 8 +// CHECK-NEXT: [[TMP11:%.*]] = add i64 [[PGOCOUNT71]], 1 +// CHECK-NEXT: store i64 [[TMP11]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 1), align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB36]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB38]], ptr [[TMP15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR48]], ptr [[TMP16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB50]], ptr [[TMP17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB52]], ptr [[TMP18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR40]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR54]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP59:%.*]] = icmp ule i64 [[TMP6]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP59]], label [[LAND_RHSCNT:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhscnt: +// CHECK-NEXT: [[PGOCOUNT72:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 2), align 8 +// CHECK-NEXT: [[TMP19:%.*]] = add i64 [[PGOCOUNT72]], 1 +// CHECK-NEXT: store i64 [[TMP19]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 2), align 8 +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP20:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP59]], [[LAND_RHSCNT]] ], [ [[CMP59]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @foo(ptr noundef [[WIDE_PTR_PTR62]], i64 noundef [[TMP6]]) +// CHECK-NEXT: [[CMP67:%.*]] = icmp sge i64 [[TMP6]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP67]]) +// CHECK-NEXT: [[ADD_PTR68:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[TMP6]] +// CHECK-NEXT: ret void +// +void bar(void*__sized_by(len) buf, unsigned long long len) +{ + foo(buf, len); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c new file mode 100644 index 0000000000000..ac3098d476f65 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c @@ -0,0 +1,12 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=strict -fno-bounds-safety-relaxed-system-headers + +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify +// expected-no-diagnostics + +void foo(int * __counted_by(size) arr, int size) { + funcInSDK(size, arr); +} + + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c b/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c new file mode 100644 index 0000000000000..718e1d18cf766 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=legacy,legacy-incorrect,common-incorrect,common +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=strict,common -fno-bounds-safety-relaxed-system-headers +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -DTEST_COMPOUND_LITERALS -I %S/include -verify=incorrect,common-incorrect,common +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -DTEST_COMPOUND_LITERALS -I %S/include -verify=strict,common -fno-bounds-safety-relaxed-system-headers +#include diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c new file mode 100644 index 0000000000000..834f007884904 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c @@ -0,0 +1,15 @@ +#include +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +char * __counted_by(len) func(char * __counted_by(len) src_str, int len) { + int len2 = 0; + char * __counted_by(len2) dst_str; + dst_str = __unsafe_forge_bidi_indexable(char*, malloc(len), len); + len2 = len; + memcpy(dst_str, src_str, len); + return dst_str; +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h new file mode 100644 index 0000000000000..f4e3bc90f788b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h @@ -0,0 +1,244 @@ +#include + +void extFunc(int size, int arr[size]); +void extFunc3(int size, int * __null_terminated arr); // strict-note{{passing argument to parameter 'arr' here}} + +#pragma clang system_header + +extern void extFunc2(int size, int *sizeLessArr); + +// CHECK: |-FunctionDecl [[func_extFunc:0x[^ ]+]] {{.+}} extFunc +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc3:0x[^ ]+]] {{.+}} extFunc3 +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc2:0x[^ ]+]] {{.+}} 'void (int, int *)' +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_sizeLessArr:0x[^ ]+]] + +static inline void funcInSDK(int size, int arr[size]) { + extFunc(size, arr); + extFunc2(size, arr); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_extFunc]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_extFunc2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +static inline void funcInSDK2(int size, int *sizeLessArr) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(size)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK(size, sizeLessArr); +} + +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_sizeLessArr_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sizeLessArr_1]] +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' + +static inline void funcInSDK3(int size, int arr[size]) { + funcInSDK(size+1, arr); + // strict-error@+2{{passing 'int *__single __counted_by(size)' (aka 'int *__single') to parameter of incompatible type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // strict-note@21{{passing argument to parameter 'arr' here}} + extFunc3(size+1, arr); + // strict-error@+1{{assignment to 'size' requires corresponding assignment to 'int *__single __counted_by(size)' (aka 'int *__single') 'arr'; add self assignment 'arr = arr' if the value has not changed}} + size++; +} + +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size + 1 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size + 1' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __terminated_by(0))' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_extFunc3]] +// CHECK: | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue + +static void tmp() { +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h new file mode 100644 index 0000000000000..8bfe1138a9aa1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h @@ -0,0 +1,162 @@ +// Pretend this is a system header +#pragma clang system_header +#include + +int* get_unspecified_ptr(void); +int* __unsafe_indexable get_unsafe_ptr(void); +// strict-note@+1 2{{passing argument to parameter 'ptr' here}} +void receive_cb(int* __counted_by(count) ptr, int count); + +// TODO: The `incorrect-error`s and `common-incorrect-error`s should not be emitted. +// They are a result of a bug +// and we emit the diagnostic rather than crashing (rdar://139815437). + +// FIXME: The `FIXME-common-incorrect-error`` should be emitted but aren't for +// some reason (rdar://140145190). + +inline int* __counted_by(count) inline_header_func_get_unspecified_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + // strict-error@+2{{returning 'int *' from a function with incompatible result type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + return get_unspecified_ptr(); +} + +inline int* __counted_by(count) inline_header_func_get_unsafe_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + // strict-error@+2{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + return get_unsafe_ptr(); +} + +inline int* __counted_by(count) inline_header_ret_explicit_unspecified_cast_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (int*)0; +} + + +inline int* __counted_by(count) inline_header_ret_explicit_unsafe_indexable_cast_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (int* __unsafe_indexable)0; +} + +inline int* __counted_by(count) inline_header_ret_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return 0; +} + +inline int* __counted_by(count) inline_header_ret_void_star_unspecified_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (void*)0; +} + +inline int* __counted_by(count) inline_header_ret_void_star_unsafe_indexable_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (void* __unsafe_indexable) 0; +} + +inline void inline_header_call_receive_cb_cast_0(void) { + receive_cb((int*)0, 0); +} + +inline void inline_header_call_receive_cb_cast_unspecified_ptr(void) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + receive_cb(get_unspecified_ptr(), 0); +} + +inline void inline_header_call_receive_cb_cast_unsafe_ptr(void) { + // strict-error@+1{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + receive_cb(get_unsafe_ptr(), 0); +} + +inline void inline_header_assign_local_cb_cast_0(void) { + int count = 0; + int* __counted_by(count) local = (int*)0; + + local = (int*)0; + count = 0; +} + + +void side_effect(void); + + +inline void inline_header_init_local_cb_unspecified_ptr(void) { + // common-error@+1{{local variable count must be declared right next to its dependent decl}} + int count = 0; + // strict-error@+3{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // common-error@+2{{local variable local must be declared right next to its dependent decl}} + // common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + int* __counted_by(count) local = get_unspecified_ptr(); +} + +inline void inline_header_assign_local_cb_unspecified_ptr(void) { + int count2 = 0; + int* __counted_by(count2) local2; + + side_effect(); + + // strict-error@+3{{assigning to 'int *__single __counted_by(count2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // FIXME: The errors in the previous function stop this error from appearing for some reason. + // FIXME-common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + local2 = get_unspecified_ptr(); + // strict-error@+1{{assignment to 'count2' requires corresponding assignment to 'int *__single __counted_by(count2)' (aka 'int *__single') 'local2'; add self assignment 'local2 = local2' if the value has not changed}} + count2 = 0; +} + +inline void inline_header_init_local_cb_unsafe_ptr(void) { + // common-error@+1{{local variable count must be declared right next to its dependent decl}} + int count = 0; + // strict-error@+3{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // common-error@+2{{local variable local must be declared right next to its dependent decl}} + // common-incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + int* __counted_by(count) local = get_unsafe_ptr(); +} + +inline void inline_header_assign_local_cb_unsafe_ptr(void) { + int count2 = 0; + int* __counted_by(count2) local2; + + side_effect(); + + // strict-error@+3{{assigning to 'int *__single __counted_by(count2)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // FIXME: The errors in the previous functions stop this error from appearing for some reason. + // FIXME-common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + local2 = get_unsafe_ptr(); + // strict-error@+1{{assignment to 'count2' requires corresponding assignment to 'int *__single __counted_by(count2)' (aka 'int *__single') 'local2'; add self assignment 'local2 = local2' if the value has not changed}} + count2 = 0; +} + +#ifdef TEST_COMPOUND_LITERALS + +struct simple_cb { + int count; + int* __counted_by(count) ptr; +}; + + +inline void inline_header_compound_literal_unspecified_ptr(struct simple_cb s, int* unspecified_ptr) { + // FIXME: This diagnostic isn't emitted when there are diagnostics from other functions. + // strict-error@+2{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // fixme-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + s = (struct simple_cb){.count = 0, .ptr = unspecified_ptr}; +} + +inline void inline_header_compound_literal_unspecified_cast_0(struct simple_cb s) { + s = (struct simple_cb){.count = 0, .ptr = (int*)0}; +} + +inline void inline_header_compound_literal_unsafe_indexable_cast_0(struct simple_cb s) { + s = (struct simple_cb){.count = 0, .ptr = (int* __unsafe_indexable)0}; +} + +inline void inline_header_compound_literal_unsafe_indexable_ptr(struct simple_cb s, int* __unsafe_indexable unsafe_indexable_ptr) { + // strict-error@+2{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + s = (struct simple_cb){.count = 0, .ptr = unsafe_indexable_ptr}; +} + +#endif diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h new file mode 100644 index 0000000000000..c4e61a3fa57a6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h @@ -0,0 +1,24 @@ +#pragma clang system_header + +static inline void memcpy(void *__restrict__ dst, void *__restrict__ src, int size) { + __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy2(void *__restrict__ dst, void *__restrict__ src, int size) { + return __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy3(void *__restrict__ dst, void *__restrict__ src, int size) { + void * tmp = __builtin_memcpy(dst, src, size); + return tmp; +} + +static inline void* malloc(int size) { + return __builtin_malloc(size); +} + +static inline void* malloc2(int size) { + void *tmp = __builtin_malloc(size); + return tmp; +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h new file mode 100644 index 0000000000000..797da3ba99c7a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h @@ -0,0 +1,5 @@ +#pragma once + +inline void increment_unsafe_p(int *p) { + p++; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h new file mode 100644 index 0000000000000..0219965228e71 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h @@ -0,0 +1,19 @@ +#include +#include + +// both-note@+2{{passing argument to parameter 'p' here}} +// strict-note@+1{{passing argument to parameter 'p' here}} +static inline int * __single funcAdopted(int * __single p) { + return p; +} + +#pragma clang system_header + +static inline int* funcSDK(intptr_t x) { + if (x % 128) + // both-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return funcAdopted(x); + else + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcAdopted((int*)x); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h new file mode 100644 index 0000000000000..12dcd459e6239 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h @@ -0,0 +1,6 @@ +#ifdef EXTINCLUDE2 +#include +#else +#include "struct-fields-ext.h" +#endif + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h new file mode 100644 index 0000000000000..18fdd09dcdeca --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h @@ -0,0 +1,101 @@ +#pragma clang system_header + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct bar { + int *__ended_by(end) p; + int *end; +}; + +static inline struct foo funcInSDK1(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct foo f = { p, count }; + return f; +} + +static inline struct foo funcInSDK2(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct foo){ p, count }; +} + +static inline struct foo funcInSDK3(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; // This results in a RecoveryExpr, so later analysis cannot see the assignment. + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + + +static inline struct foo funcInSDK4(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; + return f; +} + +static inline struct foo funcInSDK5(int *p, int count) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + +static inline struct bar funcInSDK6(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct bar b = { p, end }; + return b; +} + +static inline struct bar funcInSDK7(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct bar){ p, end }; +} + +static inline struct bar funcInSDK8(int *p, int *end) { + struct bar b; + //strict-error@+1{{assigning to 'int *__single __ended_by(end)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.p = p; + //strict-error@+1{{assigning to 'int *__single /* __started_by(p) */ ' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.end = end; + return b; +} + +static inline struct bar funcInSDK9(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.p' requires corresponding assignment to 'b.end'; add self assignment 'b.end = b.end' if the value has not changed}} + b.p = in.p; + return b; +} + +static inline struct bar funcInSDK10(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.end' requires corresponding assignment to 'b.p'; add self assignment 'b.p = b.p' if the value has not changed}} + b.end = in.end; + return b; +} + +static inline struct foo funcInSDK11(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p' requires corresponding assignment to 'f.count'; add self assignment 'f.count = f.count' if the value has not changed}} + f.p = in.p; + return f; +} + +static inline struct foo funcInSDK12(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = in.count; + return f; +} + +static tmp() { +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h new file mode 100644 index 0000000000000..070f52e923a24 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h @@ -0,0 +1,13 @@ +#include + +//strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(char *__sized_by(4) foo, char *__sized_by(5) bar); + + +#pragma clang system_header + +void funcInSDK(char *ptr, char * __bidi_indexable bidi) { + //strict-error@+1{{passing 'char *' to parameter of incompatible type 'char *__single __sized_by(4)' (aka 'char *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(ptr, bidi); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h new file mode 100644 index 0000000000000..31bf896e49502 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h @@ -0,0 +1,44 @@ +#include + +typedef const char * __null_terminated my_str_explicit_t; +typedef const char * my_str_implicit_t; +typedef int * __null_terminated my_nt_int_ptr_t; +typedef int * my_int_ptr_t; + +// both-error@+1{{'__counted_by' inside typedef is only allowed for function type}} +typedef int * __counted_by(4) ivec4_t; // If this is ever allowed we need to handle it for system headers. + +#pragma clang system_header + +static inline my_str_implicit_t funcInSDK1(const char *p) { + my_str_implicit_t str = p; + return str; + return p; +} + +static inline my_str_explicit_t funcInSDK2(const char *p) { + //strict-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_str_explicit_t str = p; + return str; + //strict-error@+1{{returning 'const char *' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +static inline my_nt_int_ptr_t funcInSDK3(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_nt_int_ptr_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +typedef my_int_ptr_t __null_terminated nt_local_t; + +static inline nt_local_t funcInSDK4(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nt_local_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h new file mode 100644 index 0000000000000..561dc1051d12d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h @@ -0,0 +1,5 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h new file mode 100644 index 0000000000000..3d44db2f51a52 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h @@ -0,0 +1,216 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; + +// RELAXED: |-VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + sizedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + valueTerminatedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + // This should result in an unsafe BoundsSafetyPointerCast rdar://99202425 +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +void funcInSDK2(int * __single __terminated_by(2) safePointer) { + sizedGlobal = safePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *__single __terminated_by(2)' (aka 'int *__single') requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + valueTerminatedGlobal = safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +void funcInSDK3(int * unsafePointer) { + unsafePointer = sizedGlobal; + unsafePointer = valueTerminatedGlobal; + unsafePointer = bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_2]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_3]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-IntegerLiteral {{.+}} 2 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +void funcInSDK4(int * __single __terminated_by(2) safePointer) { + safePointer = sizedGlobal; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *__single __sized_by(2)' (aka 'int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + safePointer = valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_4]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_5]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: `-CallExpr +// MAINCHECK: |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: `-DeclRefExpr {{.+}} [[var_term]] + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h new file mode 100644 index 0000000000000..e0e0269f51bde --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h @@ -0,0 +1,9 @@ +#include + +#pragma clang system_header + +// strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(int *__sized_by(4) foo); +void funcWithoutAnnotation(int * foo); +extern int * __single safeGlobal; +extern int * unsafeGlobal; diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h new file mode 100644 index 0000000000000..ff58385741e39 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h @@ -0,0 +1,308 @@ +#include + +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_safeGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// STRICT: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// STRICT: VarDecl [[var_safeGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safeGlobal = unsafePointer; + unsafeGlobal = unsafePointer; + unsafePointer = safeGlobal; + unsafePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK2(int * unsafePointer) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __sized_by(4)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(unsafePointer); + funcWithoutAnnotation(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(4)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | | | `-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// strict-note@+1{{passing argument to parameter 'safePointer' here}} +void funcInSDK3(int * __single safePointer) { + safeGlobal = safePointer; + unsafeGlobal = safePointer; + safePointer = safeGlobal; + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK4(int * __single safePointer) { + funcWithAnnotation(safePointer); + funcWithoutAnnotation(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single' +// RELAXED: | | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsCheckExpr {{.+}} 'safePointer <= __builtin_get_pointer_upper_bound(safePointer) && __builtin_get_pointer_lower_bound(safePointer) <= safePointer && 4 <= (char *)__builtin_get_pointer_upper_bound(safePointer) - (char *)safePointer && 0 <= 4' +// STRICT: | | | | |-CallExpr +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// STRICT: | | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} lower +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'safePointer' 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | | | `-IntegerLiteral {{.+}} 0 +// STRICT: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | `-IntegerLiteral {{.+}} 4 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +void funcInSDK5(int * unsafePointer) { + funcInSDK(unsafePointer); + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK3(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +void funcInSDK6(int * __single safePointer) { + funcInSDK(safePointer); + funcInSDK3(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h new file mode 100644 index 0000000000000..484efba4f7fc9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h @@ -0,0 +1,177 @@ +#include + +// RELAXED: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +int * __unsafe_indexable funcInSDK(int * __unsafe_indexable unsafePointer) { + return unsafePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +int * __unsafe_indexable funcInSDK2(int * __single __terminated_by(2) safePointer) { + return safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +int * __single funcInSDK3(int * __unsafe_indexable unsafePointer) { + return unsafePointer; // strict-error{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +int * __unsafe_indexable funcInSDK4(void) { + return sizedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-MaterializeSequenceExpr {{.+}} +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | `-IntegerLiteral {{.+}} 2 +// STRICT: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +int * __unsafe_indexable funcInSDK5(void) { + return valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +int * __unsafe_indexable funcInSDK6(void) { + return bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK5]] +// MAINCHECK: `-CallExpr +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: `-DeclRefExpr {{.+}} [[func_funcInSDK6]] diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h new file mode 100644 index 0000000000000..4e46443003dca --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h @@ -0,0 +1,17 @@ +#include +#include + +#pragma clang system_header + +typedef void * (*variable_length_function)(va_list args); +static inline void* call_func_internal(variable_length_function f, va_list args) { + return f(args); +} + +static inline void* call_func(variable_length_function f, ...) { + va_list ap; + + va_start(ap, f); + return call_func_internal(f, ap); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c new file mode 100644 index 0000000000000..7b3714bf779b7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c @@ -0,0 +1,12 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +int * func(intptr_t y) { + // both-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcSDK(y); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c b/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c new file mode 100644 index 0000000000000..04d1abc422b59 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c @@ -0,0 +1,23 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * a, int * b, struct bar in, struct foo in2) { + funcInSDK1(a, *b); + funcInSDK2(a, *b); + funcInSDK3(a, *b); + funcInSDK4(a, *b); + funcInSDK5(a, *b); + funcInSDK6(a, b); + funcInSDK7(a, b); + funcInSDK8(a, b); + funcInSDK9(in); + funcInSDK10(in); + funcInSDK11(in2); + funcInSDK12(in2); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c new file mode 100644 index 0000000000000..3b0a270d74016 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c @@ -0,0 +1,12 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c b/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c new file mode 100644 index 0000000000000..1ca726349ad25 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c @@ -0,0 +1,15 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(const char * a, int * b) { + funcInSDK1(a); + funcInSDK2(a); + funcInSDK3(b); + funcInSDK4(b); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c new file mode 100644 index 0000000000000..541601e012a3b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c @@ -0,0 +1,15 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(term); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c new file mode 100644 index 0000000000000..a297bb11491d8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c @@ -0,0 +1,16 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __single safe) { + funcInSDK(unsafe); + funcInSDK2(unsafe); + funcInSDK3(safe); + funcInSDK4(safe); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c new file mode 100644 index 0000000000000..1ff886537d46f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c @@ -0,0 +1,18 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(); + funcInSDK5(); + funcInSDK6(); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c new file mode 100644 index 0000000000000..22b0ef82e3d22 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c @@ -0,0 +1,13 @@ +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +extern variable_length_function func_ptr; +typedef void * (*variable_length_function2)(va_list args); +extern variable_length_function2 func_ptr2; + +void func(char *dst_str, char *src_str, int len) { + call_func(func_ptr, dst_str, src_str, len); + call_func(func_ptr2, dst_str, src_str, len); +} diff --git a/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c b/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c new file mode 100644 index 0000000000000..64162094cc1a2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c @@ -0,0 +1,9 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#pragma clang abi_ptr_attr set(single) +#pragma clang abi_ptr_attr set(indexable) +#pragma clang abi_ptr_attr set(bidi_indexable) +#pragma clang abi_ptr_attr set(unsafe_indexable) +#pragma clang abi_ptr_attr set(nullable) // expected-error{{'nullable' cannot be set as a default pointer attribute}} diff --git a/clang/test/BoundsSafety/Sema/abi-ptr-attr.c b/clang/test/BoundsSafety/Sema/abi-ptr-attr.c new file mode 100644 index 0000000000000..9d062b4171f31 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/abi-ptr-attr.c @@ -0,0 +1,84 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s | FileCheck %s +#include + +__ptrcheck_abi_assume_single() +int *FSingle(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FSingle 'int *__single(int *__single)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__single' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__single*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'x' 'int *__single' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-UnaryOperator {{.+}} 'int *__single' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__single*__bidi_indexable' + + + +__ptrcheck_abi_assume_indexable() +int *FIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FIndexable 'int *__indexable(int *__indexable)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__indexable' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__indexable*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'x' 'int *__indexable' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__indexable' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__indexable*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__indexable*__bidi_indexable' + + +__ptrcheck_abi_assume_bidi_indexable() +int *FBidiIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FBidiIndexable 'int *__bidi_indexable(int *__bidi_indexable)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__bidi_indexable' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__bidi_indexable*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'x' 'int *__bidi_indexable' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__bidi_indexable' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__bidi_indexable*__bidi_indexable' + + + +__ptrcheck_abi_assume_unsafe_indexable() +int *FUnsafeIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: `-FunctionDecl {{.+}} FUnsafeIndexable 'int *__unsafe_indexable(int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.+}} used x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used y 'int *__unsafe_indexable*__unsafe_indexable' cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'x' 'int *__unsafe_indexable' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' lvalue prefix '*' cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' lvalue Var {{.+}} 'y' 'int *__unsafe_indexable*__unsafe_indexable' + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c b/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c new file mode 100644 index 0000000000000..f951bcb39a109 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +typedef unsigned long long size_t; +extern size_t count; +extern int glob[__attribute__((counted_by(count)))]; +extern int glob2[__attribute__((sized_by(count)))]; +// expected-error@-1 {{'sized_by' cannot apply to arrays: use 'counted_by' instead}} + +void foo(int *__attribute__((counted_by(cnt))), size_t cnt); + +void bar(void) { + foo((int *)&glob, count); + // expected-error@-1 {{cannot take address of incomplete __counted_by array}} + // expected-note@-2 {{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(count)]' (aka 'int (*)[]')}} +} + +void bar2(void) { + foo((int *)&glob2, count); + // expected-warning@-1 {{count value is not statically known: passing 'int *__single' to parameter of type 'int *__single __counted_by(cnt)' (aka 'int *__single') is invalid for any count other than 0 or 1}} + // expected-note@-2 {{count passed here}} +} diff --git a/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c b/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c new file mode 100644 index 0000000000000..f8781be5925f9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(len) buf; + int len; +}; + +struct T { + int *__counted_by(len + 1) buf; + int len; +}; + +struct U { + int *__counted_by(len) buf; + int *__counted_by(len) buf2; + int len; +}; + +struct V { + int len; + int buf[__counted_by(len)]; // expected-note 8{{referred to by count parameter here}} +}; + +int arr[10]; + +// expected-note@+1{{passing argument to parameter 'out_buf' here}} +void foo(int *out_len, int *__counted_by(*out_len) * out_buf) { + *out_len = 9; + *out_buf = arr; + return; +} + +void bar(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void * baz(struct V *v) { + int *__single ptr_to_len = &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int **ptr_to_buf = &v->buf; // expected-warning{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]')}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &v->buf; // expected-warning{{incompatible pointer types assigning to 'int *__single*__bidi_indexable' from 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]')}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + *ptr_to_len = &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + *ptr_to_len = &(*v).len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + *ptr_to_len = 100; + + foo(&v->len, &v->buf); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + int local_len = 10; + foo(&local_len, &v->buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-warning@-2{{incompatible pointer types passing 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]') to parameter of type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + // expected-note@-3{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + bar(&v->len, &v->buf); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + (void) &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &v->buf; // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + (void) &(v->len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(v->buf); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + return &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + return &v->buf; // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + return &(v->buf+2); // expected-error{{cannot take the address of an rvalue of type 'int *__bidi_indexable'}} +} + +int main() { + struct S s = {0}; + // expected-error@+1{{initializing 't.buf' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct T t = {0}; + struct U u = {0}; + + int local_len = 10; + int *__single ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + *ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_len = &struct_ptr->len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &(*struct_ptr).len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_len = 100; + + foo(&s.len, &s.buf); + foo(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t.len, &t.buf); // expected-error{{incompatible count expression (*out_len) vs. (len + 1) in argument to function}} + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&u.len, &u.buf); + bar(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c b/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c new file mode 100644 index 0000000000000..83ba33146990d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +struct S { + int *__ended_by(end) start; + int *end; +}; + +struct U { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void fun_out_end(int **out_end, int *__ended_by(*out_end) * out_start); +void fun_out_seq(int **out_end, + int *__ended_by(*out_iter) * out_start, + int *__ended_by(*out_end) * out_iter); + +void fun_in_start_out_end(int *__ended_by(*out_end) start, int **out_end) { + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + fun_out_end(out_end, &start); +} + +void fun_no_out_end(int **no_out_end, int **no_out_start); + +void test() { + struct S s = {0}; + struct U u = {0}; + + int *__single local_end = 0; + int **__single ptr_to_end = &s.end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_start = &s.start; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***ptr_ptr_to_end = &ptr_to_end; + ptr_to_end = &s.end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_start = &s.start; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_end = &struct_ptr->end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_end = &(*struct_ptr).end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_end = 0; + + fun_out_end(&s.end, &s.start); + // expected-error@+1{{type of 'local_end', 'int *__single', is incompatible with parameter of type 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single')}} + fun_out_end(&local_end, &s.start); + // expected-error@+1{{passing address of 'end' as an indirect parameter; must also pass 'iter' or its address because the type of 'iter', 'int *__single __ended_by(end) /* __started_by(start) */ ' (aka 'int *__single'), refers to 'end'}} + fun_out_end(&u.end, &u.start); + fun_out_seq(&u.end, &u.start, &u.iter); + // expected-error@+1{{type of 'start', 'int *__single __ended_by(iter)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(*out_iter) */ ' (aka 'int *__single')}} + fun_out_seq(&u.start, &u.iter, &u.end); + // expected-error@+1{{passing address of 'start' as an indirect parameter; must also pass 'iter' or its address because the type of 'start', 'int *__single __ended_by(iter)' (aka 'int *__single'), refers to 'iter'}} + fun_out_seq(&s.end, &u.start, &s.start); + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single*__single'}} + fun_no_out_end(&s.end, &s.start); +} diff --git a/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c b/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c new file mode 100644 index 0000000000000..a2e28e4b88fe3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); +int get_len(); + +int foo() { + int a = 10; + int b = sizeof(int); + int *ptr = my_calloc(get_len(), b); + ptr = my_calloc(a, get_len()); + return ptr[10]; +} + +// expected-no-diagnostics \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..0b6b4f55b5c31 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,75 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len); + +void call_param_with_count(void) { + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + param_with_count(0, 0); + param_with_count(0, 2); + // expected-error@+1{{passing null to parameter 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single') with count value of 1 always fails}} + param_with_count(0, 3); + + // expected-note@+1{{'arr' declared here}} + int arr[10] = {0}; + param_with_count(arr, 12); + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + param_with_count(arr, 0); + // expected-error@+1{{passing array 'arr' (which has 10 elements) to parameter 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single') with count value of 11 always fails}} + param_with_count(arr, 13); + param_with_count(arr, 2); +} + +void param_with_count_size(int *__counted_by(size * count) buf, int size, int count); + +void call_param_with_count_size(void) { + // expected-error@+1{{negative count value of -1 for 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single')}} + param_with_count_size(0, -1, 1); + param_with_count_size(0, 0, 0); + // expected-error@+1{{passing null to parameter 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single') with count value of 6 always fails}} + param_with_count_size(0, 3, 2); + + // expected-note@+1{{'arr' declared here}} + int arr[10] = {0}; + param_with_count_size(arr, 2, 5); + // expected-error@+1{{negative count value of -1 for 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single')}} + param_with_count_size(arr, 1, -1); + // expected-error@+1{{passing array 'arr' (which has 10 elements) to parameter 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single') with count value of 15 always fails}} + param_with_count_size(arr, 3, 5); + param_with_count_size(arr, 0, 0); +} + +// params sharing count but different expr and the callers +void param_with_shared_size(void *__sized_by(size - 1) buf1, void *__sized_by(size - 2) buf2, int size); + +void call_param_with_shared_size(void) { + // expected-error@+1{{negative size value of -1 for 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single')}} + param_with_shared_size(0, 0, 0); + // expected-error@+1{{negative size value of -1 for 'buf2' of type 'void *__single __sized_by(size - 2)' (aka 'void *__single')}} + param_with_shared_size(0, 0, 1); + // expected-error@+1{{passing null to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 1 always fails}} + param_with_shared_size(0, 0, 2); + // expected-error@+1{{passing null to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 9 always fails}} + param_with_shared_size(0, 0, 10); + + // expected-note@+1{{'arr1' declared here}} + char arr1[9] = {0}; + char arr2[8] = {0}; + // expected-error@+1{{negative size value of -1 for 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single')}} + param_with_shared_size(arr1, arr2, 0); + // expected-error@+1{{negative size value of -1 for 'buf2' of type 'void *__single __sized_by(size - 2)' (aka 'void *__single')}} + param_with_shared_size(arr1, arr2, 1); + param_with_shared_size(arr1, arr2, 2); + param_with_shared_size(arr1, arr2, 10); + // expected-error@+1{{passing array 'arr1' (which has 9 bytes) to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 11 always fails}} + param_with_shared_size(arr1, arr2, 12); +} + +// returns with count and the callers +void *__sized_by(count * size) return_with_count_size(int count, int size); + +void call_return_with_count_size(void) { + // FIXME: rdar://103368466 + void *buf = return_with_count_size(-1, 1); +} diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c new file mode 100644 index 0000000000000..fe611a6e0512d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c @@ -0,0 +1,76 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +int len; +int *__counted_by(len - 2) buf; +// expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} +int *__counted_by_or_null(len - 2) buf_n; + +int len2 = 2; +int *__counted_by(len2 - 2) buf2; +int *__counted_by_or_null(len2 - 2) buf2_n; + +int len3 = 0; +int *__counted_by(len3 - 2) buf3; +// expected-error@-1{{negative count value of -2 for 'buf3' of type 'int *__single __counted_by(len3 - 2)' (aka 'int *__single')}} +int *__counted_by_or_null(len3 - 2) buf3_n; + +int len4 = 0; +int *__counted_by(len4 + 2) buf4; +// expected-error@-1{{implicitly initializing 'buf4' of type 'int *__single __counted_by(len4 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len4 + 2) buf4_n; + +int len5 = 0; +int *__counted_by(len5 + 2) buf5 = 0; +// expected-error@-1{{initializing 'buf5' of type 'int *__single __counted_by(len5 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len5 + 2) buf5_n = 0; + +int len5_1; +int *__counted_by(len5_1 + 2) buf5_1 = 0; +// expected-error@-1{{initializing 'buf5_1' of type 'int *__single __counted_by(len5_1 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len5_1 + 2) buf5_1_n = 0; + +int *len6; +int *__counted_by(*len6) buf6; +// expected-error@-1{{dereference operator in '__counted_by' is only allowed for function parameters}} +int *__counted_by_or_null(*len6) buf6_n; +// expected-error@-1{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + +int *len6_1; +int *__counted_by(*len6_1 + 2) buf6_1; +// expected-error@-1{{invalid argument expression to bounds attribute}} +int *__counted_by_or_null(*len6_1 + 2) buf6_1_n; +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int len7_1; +int len7_2; +int *__counted_by(len7_1 * 2 + len7_2 * 4) buf7; +int *__counted_by_or_null(len7_1 * 2 + len7_2 * 4) buf7_n; + +int len8_1; +int len8_2; +int *__counted_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; +// expected-error@-1{{implicitly initializing 'buf8' of type 'int *__single __counted_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} +int *__counted_by_or_null(len8_1 * 2 + len8_2 * 4 + 1) buf8_n; + +int len9_1 = 1; +int len9_2 = 3; +int *__counted_by(len9_1 * len9_2) buf9; +// expected-error@-1{{implicitly initializing 'buf9' of type 'int *__single __counted_by(len9_1 * len9_2)' (aka 'int *__single') and count value of 3 with null always fails}} +int *__counted_by_or_null(len9_1 * len9_2) buf9_n; + +int len10_1; +int len10_2 = 2; +int *__counted_by(len10_1 * len10_2) buf10; +int *__counted_by_or_null(len10_1 * len10_2) buf10_n; + +int len11_1; +int len11_2 = 2; +int *__counted_by(len11_1 * len11_2 + 1) buf11; +// expected-error@-1{{implicitly initializing 'buf11' of type 'int *__single __counted_by(len11_1 * len11_2 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} +int *__counted_by_or_null(len11_1 * len11_2 + 1) buf11_n; + +// don't complain about extern variables +extern int ext_len; +extern int *__counted_by(ext_len - 2) ext_buf; diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c new file mode 100644 index 0000000000000..df88c410d1537 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c @@ -0,0 +1,214 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + + +void test_counted_by(void) { + int len; + int *__counted_by(len - 2) buf; + // expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__counted_by(len2 - 2) buf2; + + int len3 = 0; + int *__counted_by(len3 - 2) buf3; + // expected-error@-1{{negative count value of -2 for 'buf3' of type 'int *__single __counted_by(len3 - 2)' (aka 'int *__single')}} + + int len4 = 0; + int *__counted_by(len4 + 2) buf4; + // expected-error@-1{{implicitly initializing 'buf4' of type 'int *__single __counted_by(len4 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int len5 = 0; + int *__counted_by(len5 + 2) buf5 = 0; + // expected-error@-1{{initializing 'buf5' of type 'int *__single __counted_by(len5 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int len5_1; + int *__counted_by(len5_1 + 2) buf5_1 = 0; + // expected-error@-1{{initializing 'buf5_1' of type 'int *__single __counted_by(len5_1 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int *len6; + int *__counted_by(*len6) buf6; + // expected-error@-1{{dereference operator in '__counted_by' is only allowed for function parameters}} + + int *len6_1; + int *__counted_by(*len6_1 + 2) buf6_1; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len7_1; + int len7_2; + int *__counted_by(len7_1 * 2 + len7_2 * 4) buf7; + + int len8_1; + int len8_2; + int *__counted_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; + // expected-error@-1{{implicitly initializing 'buf8' of type 'int *__single __counted_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + + int len9_1 = 1; + int len9_2 = 3; + int *__counted_by(len9_1 * len9_2) buf9; + // expected-error@-1{{implicitly initializing 'buf9' of type 'int *__single __counted_by(len9_1 * len9_2)' (aka 'int *__single') and count value of 3 with null always fails}} + + int len10_1; + int len10_2 = 2; + int *__counted_by(len10_1 * len10_2) buf10; + + int len11_1; + int len11_2 = 2; + int *__counted_by(len11_1 * len11_2 + 1) buf11; + // expected-error@-1{{implicitly initializing 'buf11' of type 'int *__single __counted_by(len11_1 * len11_2 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + + // don't complain about extern variables + extern int ext_len; + extern int *__counted_by(ext_len - 2) ext_buf; +} + +void test_counted_by_or_null(void) { + int arr[2] = {1,2}; // expected-note{{'arr' declared here}} + int len; + int *__counted_by_or_null(len - 2) buf = arr; + // expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by_or_null(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__counted_by_or_null(len2 - 2) buf2 = arr; + + int len3 = 0; + int *__counted_by_or_null(len3 + 2) buf3 = arr; + + int *len4; + int *__counted_by_or_null(*len4) buf4 = arr; + // expected-error@-1{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + + int *len4_1; + int *__counted_by_or_null(*len4_1 + 2) buf4_1 = arr; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len5_1; + int len5_2; + int *__counted_by_or_null(len5_1 * 2 + len5_2 * 4) buf5 = arr; + // expected-warning@-1{{possibly initializing 'buf5' of type 'int *__single __counted_by_or_null(len5_1 * 2 + len5_2 * 4)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + int len6_1; + int len6_2; + int *__counted_by_or_null(len6_1 * 2 + len6_2 * 4 - 1) buf6 = arr; + // expected-error@-1{{negative count value of -1 for 'buf6' of type 'int *__single __counted_by_or_null(len6_1 * 2 + len6_2 * 4 - 1)' (aka 'int *__single')}} + + int len7_1 = 1; + int len7_2 = 3; + int *__counted_by_or_null(len7_1 * len7_2) buf7 = arr; + // expected-error@-1{{initializing 'buf7' of type 'int *__single __counted_by_or_null(len7_1 * len7_2)' (aka 'int *__single') and count value of 3 with array 'arr' (which has 2 elements) always fails}} + + int len8_1; + int len8_2 = 2; + int *__counted_by_or_null(len8_1 * len8_2) buf8 = arr; + // expected-warning@-1{{possibly initializing 'buf8' of type 'int *__single __counted_by_or_null(len8_1 * len8_2)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + int len9_1; + int len9_2 = 2; + int *__counted_by_or_null(len9_1 * len9_2 + 1) buf9 = arr; +} + +void test_sized_by(void) { + int len; + void *__sized_by(len - 2) buf; + // expected-error@-1{{negative size value of -2 for 'buf' of type 'void *__single __sized_by(len - 2)' (aka 'void *__single')}} + + int len2 = 2; + void *__sized_by(len2 - 2) buf2; + + int len3 = 0; + void *__sized_by(len3 - 2) buf3; + // expected-error@-1{{negative size value of -2 for 'buf3' of type 'void *__single __sized_by(len3 - 2)' (aka 'void *__single')}} + + int len4 = 0; + void *__sized_by(len4 + 2) buf4; + // expected-error@-1{{implicitly initializing 'buf4' of type 'void *__single __sized_by(len4 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int len5 = 0; + void *__sized_by(len5 + 2) buf5 = 0; + // expected-error@-1{{initializing 'buf5' of type 'void *__single __sized_by(len5 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int len5_1; + void *__sized_by(len5_1 + 2) buf5_1 = 0; + // expected-error@-1{{initializing 'buf5_1' of type 'void *__single __sized_by(len5_1 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int *len6; + void *__sized_by(*len6) buf6; + // expected-error@-1{{dereference operator in '__sized_by' is only allowed for function parameters}} + + int *len6_1; + void *__sized_by(*len6_1 + 2) buf6_1; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len7_1; + int len7_2; + void *__sized_by(len7_1 * 2 + len7_2 * 4) buf7; + + int len8_1; + int len8_2; + void *__sized_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; + // expected-error@-1{{implicitly initializing 'buf8' of type 'void *__single __sized_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + + int len9_1 = 1; + int len9_2 = 3; + void *__sized_by(len9_1 * len9_2) buf9; + // expected-error@-1{{implicitly initializing 'buf9' of type 'void *__single __sized_by(len9_1 * len9_2)' (aka 'void *__single') and size value of 3 with null always fails}} + + int len10_1; + int len10_2 = 2; + void *__sized_by(len10_1 * len10_2) buf10; + + int len11_1; + int len11_2 = 2; + void *__sized_by(len11_1 * len11_2 + 1) buf11; + // expected-error@-1{{implicitly initializing 'buf11' of type 'void *__single __sized_by(len11_1 * len11_2 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + + // don't complain about extern variables + extern int ext_len2; + extern void *__sized_by(ext_len2 - 2) ext_buf2; +} + +void test_sized_by_or_null(void) { + int arr[2] = {1,2}; + int len; + int *__sized_by_or_null(len - 2) buf = arr; + // expected-error@-1{{negative size value of -2 for 'buf' of type 'int *__single __sized_by_or_null(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__sized_by_or_null(len2 - 2) buf2 = arr; + + int len3 = 0; + int *__sized_by_or_null(len3 + 2) buf3 = arr; + + int *len4; + int *__sized_by_or_null(*len4) buf4 = arr; + // expected-error@-1{{dereference operator in '__sized_by_or_null' is only allowed for function parameters}} + + int *len4_1; + int *__sized_by_or_null(*len4_1 + 2) buf4_1 = arr; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len5_1; + int len5_2; + int *__sized_by_or_null(len5_1 * 2 + len5_2 * 4) buf5 = arr; + // expected-warning@-1{{possibly initializing 'buf5' of type 'int *__single __sized_by_or_null(len5_1 * 2 + len5_2 * 4)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + + int len6_1; + int len6_2; + int *__sized_by_or_null(len6_1 * 2 + len6_2 * 4 - 1) buf6 = arr; + // expected-error@-1{{negative size value of -1 for 'buf6' of type 'int *__single __sized_by_or_null(len6_1 * 2 + len6_2 * 4 - 1)' (aka 'int *__single')}} + + int len7_1 = 1; + int len7_2 = 3; + int *__sized_by_or_null(len7_1 * len7_2) buf7 = arr; + + int len8_1; + int len8_2 = 2; + int *__sized_by_or_null(len8_1 * len8_2) buf8 = arr; + // expected-warning@-1{{possibly initializing 'buf8' of type 'int *__single __sized_by_or_null(len8_1 * len8_2)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + + int len9_1; + int len9_2 = 2; + int *__sized_by_or_null(len9_1 * len9_2 + 1) buf9 = arr; +} + diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c new file mode 100644 index 0000000000000..17b99d2bddac5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c @@ -0,0 +1,149 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + + +/* -------------------- + counted_by + -------------------- */ +void param_with_count(int *__counted_by(len - 2) buf, int len) { + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + buf = 0; + len = 0; +} + +void inout_count(int *__counted_by(*len) buf, int *len); + +void inout_count_buf(int *__counted_by(*len) *buf, int *len); + +void pass_argument_to_inout_count_buf(int *__counted_by(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_count_buf(&buf, &len); + // expected-error@+1{{incompatible count expression (*len) vs. (len - 1) in argument to function}} + inout_count(buf, &len); + + int arr[10]; + int len2 = 9; + int *__counted_by(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_count_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_count(buf2, &len2); + + int len3 = 1; + int *__counted_by(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __counted_by(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_count_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __counted_by(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_count(buf2, &len3); + // expected-error@+1{{incompatible count expression (*len) vs. (len3 - 1) in argument to function}} + inout_count(buf3, &len3); +} + +/* -------------------- + sized_by + -------------------- */ +void param_with_size(int *__sized_by(len - 2) buf, int len) { + // expected-error@+1{{negative size value of -2 for 'buf' of type 'int *__single __sized_by(len - 2)' (aka 'int *__single')}} + buf = 0; + len = 0; +} + +void inout_size(int *__sized_by(*len) buf, int *len); + +void inout_size_buf(int *__sized_by(*len) *buf, int *len); + +void pass_argument_to_inout_size_buf(int *__sized_by(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_size_buf(&buf, &len); + // expected-error@+1{{incompatible count expression (*len) vs. (len - 1) in argument to function}} + inout_size(buf, &len); + + int arr[10]; + int len2 = 9; + int *__sized_by(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_size_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_size(buf2, &len2); + + int len3 = 1; + int *__sized_by(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __sized_by(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_size_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __sized_by(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_size(buf2, &len3); + // expected-error@+1{{incompatible count expression (*len) vs. (len3 - 1) in argument to function}} + inout_size(buf3, &len3); +} + +/* -------------------- + counted_by_or_null + -------------------- */ +void inout_count_nullable(int *__counted_by_or_null(*len) buf, int *len); + +void inout_count_nullable_buf(int *__counted_by_or_null(*len) *buf, int *len); + +void pass_argument_to_inout_count_nullable_buf(int *__counted_by_or_null(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_count_buf(&buf, &len); + // expected-error@+1{{incompatible count expression (*len) vs. (len - 1) in argument to function}} + inout_count_nullable(buf, &len); + + int arr[10]; + int len2 = 9; + int *__counted_by_or_null(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_count_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_count_nullable(buf2, &len2); + + int len3 = 1; + int *__counted_by_or_null(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_count_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __counted_by_or_null(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_count_nullable(buf2, &len3); + // expected-error@+1{{incompatible count expression (*len) vs. (len3 - 1) in argument to function}} + inout_count_nullable(buf3, &len3); +} + +/* -------------------- + sized_by_or_null + -------------------- */ +void inout_size_nullable(int *__sized_by_or_null(*len) buf, int *len); + +void inout_size_nullable_buf(int *__sized_by_or_null(*len) *buf, int *len); + +void pass_argument_to_inout_size_nullable_buf(int *__sized_by_or_null(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_size_nullable_buf(&buf, &len); + // expected-error@+1{{incompatible count expression (*len) vs. (len - 1) in argument to function}} + inout_size_nullable(buf, &len); + + int arr[10]; + int len2 = 9; + int *__sized_by_or_null(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_size_nullable_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + inout_size_nullable(buf2, &len2); + + int len3 = 1; + int *__sized_by_or_null(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __sized_by_or_null(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_size_nullable_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression (*len) vs. (len2 + 1) in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __sized_by_or_null(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_size_nullable(buf2, &len3); + // expected-error@+1{{incompatible count expression (*len) vs. (len3 - 1) in argument to function}} + inout_size_nullable(buf3, &len3); +} diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c new file mode 100644 index 0000000000000..2007ddf6814f5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, size_t len); + +void param_with_count_size(int *__counted_by(len * size) buf, size_t len, size_t size); + +void *__sized_by(size * len) return_count_size(size_t len, size_t size); + +void *__sized_by(size * len) *return_count_size_ptr(size_t len, size_t size); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void *__sized_by(size * *len) return_inout_count_size_ptr(size_t *len, size_t size); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by(*len) return_inout_count(size_t *len); + +void *__sized_by(*len + 1) return_inout_count_1(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by_or_null(*len + 1) return_inout_count_2(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int *__counted_by_or_null(*len + 1) return_inout_count_3(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by(*len) *return_inout_count_ptr(size_t *len); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void *__sized_by(*len + 1) *return_inout_count_1_ptr(size_t *len); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void inout_buf(int *__counted_by(len * size) *buf, size_t len, size_t size); + +void inout_buf_count(int *__counted_by(*len + 1) *buf, size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void inout_count(int *__counted_by(*len - 2) buf, size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c new file mode 100644 index 0000000000000..b28ce40afe0cd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c @@ -0,0 +1,76 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +int len; +void *__sized_by(len - 2) buf; +// expected-error@-1{{negative size value of -2 for 'buf' of type 'void *__single __sized_by(len - 2)' (aka 'void *__single')}} +void *__sized_by_or_null(len - 2) buf_n; + +int len2 = 2; +void *__sized_by(len2 - 2) buf2; +void *__sized_by_or_null(len2 - 2) buf2_n; + +int len3 = 0; +void *__sized_by(len3 - 2) buf3; +// expected-error@-1{{negative size value of -2 for 'buf3' of type 'void *__single __sized_by(len3 - 2)' (aka 'void *__single')}} +void *__sized_by_or_null(len3 - 2) buf3_n; + +int len4 = 0; +void *__sized_by(len4 + 2) buf4; +// expected-error@-1{{implicitly initializing 'buf4' of type 'void *__single __sized_by(len4 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len4 + 2) buf4_n; + +int len5 = 0; +void *__sized_by(len5 + 2) buf5 = 0; +// expected-error@-1{{initializing 'buf5' of type 'void *__single __sized_by(len5 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len5 + 2) buf5_n = 0; + +int len5_1; +void *__sized_by(len5_1 + 2) buf5_1 = 0; +// expected-error@-1{{initializing 'buf5_1' of type 'void *__single __sized_by(len5_1 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len5_1 + 2) buf5_1_n = 0; + +int *len6; +void *__sized_by(*len6) buf6; +// expected-error@-1{{dereference operator in '__sized_by' is only allowed for function parameters}} +void *__sized_by_or_null(*len6) buf6_n; +// expected-error@-1{{dereference operator in '__sized_by_or_null' is only allowed for function parameters}} + +int *len6_1; +void *__sized_by(*len6_1 + 2) buf6_1; +// expected-error@-1{{invalid argument expression to bounds attribute}} +void *__sized_by_or_null(*len6_1 + 2) buf6_1_n; +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int len7_1; +int len7_2; +void *__sized_by(len7_1 * 2 + len7_2 * 4) buf7; +void *__sized_by_or_null(len7_1 * 2 + len7_2 * 4) buf7_n; + +int len8_1; +int len8_2; +void *__sized_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; +// expected-error@-1{{implicitly initializing 'buf8' of type 'void *__single __sized_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} +void *__sized_by_or_null(len8_1 * 2 + len8_2 * 4 + 1) buf8_n; + +int len9_1 = 1; +int len9_2 = 3; +void *__sized_by(len9_1 * len9_2) buf9; +// expected-error@-1{{implicitly initializing 'buf9' of type 'void *__single __sized_by(len9_1 * len9_2)' (aka 'void *__single') and size value of 3 with null always fails}} +void *__sized_by_or_null(len9_1 * len9_2) buf9_n; + +int len10_1; +int len10_2 = 2; +void *__sized_by(len10_1 * len10_2) buf10; +void *__sized_by_or_null(len10_1 * len10_2) buf10_n; + +int len11_1; +int len11_2 = 2; +void *__sized_by(len11_1 * len11_2 + 1) buf11; +// expected-error@-1{{implicitly initializing 'buf11' of type 'void *__single __sized_by(len11_1 * len11_2 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} +void *__sized_by_or_null(len11_1 * len11_2 + 1) buf11_n; + +// don't complain about extern variables +extern int ext_len; +extern int *__sized_by(ext_len - 2) ext_buf; diff --git a/clang/test/BoundsSafety/Sema/array-as-count.c b/clang/test/BoundsSafety/Sema/array-as-count.c new file mode 100644 index 0000000000000..d327826a4a948 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-as-count.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void ok_counted_by(int buf[__counted_by(count)], int count); +void ok_sized_by(int buf[__sized_by(size)], int size); + +void ok_constant_array(void) { + int foo[4]; +} + +void ok_variable_array(void) { + int count = 4; + int foo[count]; +} + + +// this is OK: `buf` becomes indexable +__ptrcheck_abi_assume_indexable() +void ok_abi_indexable(int buf[]); + + +// this is not OK: `buf` becomes single +__ptrcheck_abi_assume_single() +typedef int incomplete_int_array_t[]; + +void fail_typedef_decay_to_single(incomplete_int_array_t array); // expected-error{{parameter of array type 'incomplete_int_array_t' (aka 'int[]') decays to a __single pointer, and will not allow arithmetic}} \ + expected-note{{add a count attribute within the declarator brackets or convert the parameter to a pointer with a count or size attribute}} + +void fail_decay_to_single(int buf[]); // expected-error{{parameter of array type 'int[]' decays to a __single pointer, and will not allow arithmetic}} \ + expected-note{{add a count attribute within the declarator brackets or convert the parameter to a pointer with a count or size attribute}} + +void fail_fixed_size(int buf[__counted_by(count) 4], int count); // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +void fail_variable_size(int count, int buf[__counted_by(count) count]); // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + +void fail_local_array_constant(void) { + int foo[__counted_by(4)] = {1, 2, 3, 4}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +} + +void fail_local_array_variable(void) { + int count = 4; + int foo[__counted_by(4) count]; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +} + +struct fail_struct_fixed_size { + int count; + int elems[__counted_by(count) 5]; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +}; + +struct fail_struct_sized_by { + int count; + int elems[__sized_by(count)]; // expected-error{{'__sized_by' cannot apply to arrays: use 'counted_by' instead}} +}; + +// It's not possible to create a struct field with a variable-length array +// inside with clang, so this case isn't tested. (GCC lets you do that using a +// local struct with a field sized by a local variable, but Clang has a +// diagnostic that says this will "never be supported".) + +// These are OK: +typedef int (__array_decay_discards_count_in_parameters int_array_but_decays_t)[10]; +void ok_typedef_count_discarded(int_array_but_decays_t array); +void ok_count_discarded(int (__array_decay_discards_count_in_parameters array)[5]); +void ok_count_discarded_count_attr(int_array_but_decays_t __counted_by(count) array, int count); diff --git a/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c b/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c new file mode 100644 index 0000000000000..da3280626bc00 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include +#include diff --git a/clang/test/BoundsSafety/Sema/array-pointer.c b/clang/test/BoundsSafety/Sema/array-pointer.c new file mode 100644 index 0000000000000..b9354ace9c39c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-pointer.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int foo(void) { + int array[3] = {1, 2, 3}; + int(*ap)[3] = &array; + return (*ap)[0]; +} + +int bar(void) { + int array[3] = {1, 2, 3}; + int *p = array; + // expected-warning@+1{{incompatible pointer types initializing 'int (*__bidi_indexable)[3]' with an expression of type 'int *__bidi_indexable'}} + int(*ap)[3] = p; + return (*ap)[0]; +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c b/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c new file mode 100644 index 0000000000000..c5dd2ab9f9f1b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr1 <- val1 + int *_Atomic __single ptr1; + int *__unsafe_indexable val1; + __c11_atomic_store(&ptr1, val1, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr2 <- val2 + int *_Atomic __single ptr2; + int *__unsafe_indexable val2; + __c11_atomic_exchange(&ptr2, val2, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected3 <- ptr3 + int *_Atomic __unsafe_indexable ptr3; + int *__single expected3; + int *__unsafe_indexable desired3; + __c11_atomic_compare_exchange_strong(&ptr3, &expected3, desired3, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr4 <- desired4 + int *_Atomic __single ptr4; + int *__single expected4; + int *__unsafe_indexable desired4; + __c11_atomic_compare_exchange_strong(&ptr4, &expected4, desired4, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected5 <- ptr5 + int *_Atomic __unsafe_indexable ptr5; + int *__single expected5; + int *__unsafe_indexable desired5; + __c11_atomic_compare_exchange_weak(&ptr5, &expected5, desired5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr6 <- desired6 + int *_Atomic __single ptr6; + int *__single expected6; + int *__unsafe_indexable desired6; + __c11_atomic_compare_exchange_weak(&ptr6, &expected6, desired6, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h b/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h new file mode 100644 index 0000000000000..7e316342eb38a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#pragma clang system_header + +#include + +void unspecified(void) { + int x; + int *_Atomic p = &x; + int *q; + + p++; + p--; + ++p; + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *)' and 'int'}} + p -= 42; + + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11.c b/clang/test/BoundsSafety/Sema/atomic-ops-c11.c new file mode 100644 index 0000000000000..f86b68199109f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11.c @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include "atomic-ops-c11-system.h" + +void unsafe_indexable(void) { + int x; + int *_Atomic __unsafe_indexable p = &x; + int *__unsafe_indexable q; + + p++; + p--; + ++p; + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__unsafe_indexable)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__unsafe_indexable)' and 'int'}} + p -= 42; + + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void single(void) { + int x; + int *_Atomic __single p = &x; + int *__single q; + + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + p++; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + p--; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + ++p; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__single)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__single)' and 'int'}} + p -= 42; + + // expected-error@+1{{array subscript on single pointer 'p' must use a constant index of 0 to be in bounds}} + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('_Atomic(int *__single) *__bidi_indexable' invalid)}} + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('_Atomic(int *__single) *__bidi_indexable' invalid)}} + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c new file mode 100644 index 0000000000000..8dfca5e32f3ac --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+4{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // ret1 <- ptr1 + int *__unsafe_indexable ptr1; + int *__single ret1; + __atomic_load(&ptr1, &ret1, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr2 <- val2 + int *__single ptr2; + int *__unsafe_indexable val2; + __atomic_store_n(&ptr2, val2, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + // ptr3 <- val3 + int *__single ptr3; + int *__unsafe_indexable val3; + __atomic_store(&ptr3, &val3, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr4 <- val4 + int *__single ptr4; + int *__unsafe_indexable val4; + __atomic_exchange_n(&ptr4, val4, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*'; use explicit cast to perform this conversion}} + // ptr5 <- val5 + int *__single ptr5; + int *__unsafe_indexable val5; + int *__single ret5; + __atomic_exchange(&ptr5, &val5, &ret5, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*__bidi_indexable'; use explicit cast to perform this conversion}} + // ret6 <- ptr6 + int *__unsafe_indexable ptr6; + int *__unsafe_indexable val6; + int *__single ret6; + __atomic_exchange(&ptr6, &val6, &ret6, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected7 <- ptr7 + int *__unsafe_indexable ptr7; + int *__single expected7; + int *__unsafe_indexable desired7; + __atomic_compare_exchange_n(&ptr7, &expected7, desired7, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr8 <- desired8 + int *__single ptr8; + int *__single expected8; + int *__unsafe_indexable desired8; + __atomic_compare_exchange_n(&ptr8, &expected8, desired8, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected9 <- ptr9 + int *__unsafe_indexable ptr9; + int *__single expected9; + int *__unsafe_indexable desired9; + __atomic_compare_exchange(&ptr9, &expected9, &desired9, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + // ptr10 <- desired10 + int *__single ptr10; + int *__single expected10; + int *__unsafe_indexable desired10; + __atomic_compare_exchange(&ptr10, &expected10, &desired10, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h new file mode 100644 index 0000000000000..65fc84ee9fdbb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +#pragma clang system_header + +void unspecified(void) { + int x; + int * p = &x; + int * q; + int * r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c b/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c new file mode 100644 index 0000000000000..6ffb428817b08 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c @@ -0,0 +1,584 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include "atomic-ops-gnu-system.h" + +void unsafe_indexable(void) { + int x; + int *__unsafe_indexable p = &x; + int *__unsafe_indexable q; + int *__unsafe_indexable r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void single(void) { + int x; + int *__single p = &x; + int *__single q; + int *__single r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void indexable(void) { + int x; + int *__indexable p = &x; + int *__indexable q; + int *__indexable r; + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void bidi_indexable(void) { + int x; + int *__bidi_indexable p = &x; + int *__bidi_indexable q; + int *__bidi_indexable r; + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void counted_by(void) { + int x; + int *__counted_by(1) p = &x; + int *__counted_by(1) q = &x; + int *__counted_by(1) r = &x; + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void sized_by(void) { + int x; + int *__sized_by(4) p = &x; + int *__sized_by(4) q = &x; + int *__sized_by(4) r = &x; + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void counted_by_or_null(void) { + int x; + int *__counted_by_or_null(1) p = &x; + int *__counted_by_or_null(1) q = &x; + int *__counted_by_or_null(1) r = &x; + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void sized_by_or_null(void) { + int x; + int *__sized_by_or_null(4) p = &x; + int *__sized_by_or_null(4) q = &x; + int *__sized_by_or_null(4) r = &x; + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void ended_by(int * x, int * y, int * z, int * __ended_by(x) p, int * __ended_by(y) q, int *__ended_by(z) r) { + // expected-error@+2{{assignment to 'int *__single __ended_by(y)' (aka 'int *__single') 'q' requires corresponding assignment to 'y'; add self assignment 'y = y' if the value has not changed}} + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+2{{assignment to 'int *__single __ended_by(y)' (aka 'int *__single') 'q' requires corresponding assignment to 'y'; add self assignment 'y = y' if the value has not changed}} + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c b/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c new file mode 100644 index 0000000000000..824ca46d86826 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) p2; + + // The nested pointers should be __single. + int *_Atomic *_Atomic __unsafe_indexable p3; + _Atomic(int *) *_Atomic __unsafe_indexable p4; + + // There shouldn't be an error about __bidi_indexable, since the attribute + // is replaced by __single when __counted_by is handled. + int len; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(len) p5; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(len) _Atomic p6; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) p7; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) p8; +} diff --git a/clang/test/BoundsSafety/Sema/atomic-types.c b/clang/test/BoundsSafety/Sema/atomic-types.c new file mode 100644 index 0000000000000..4c61b0a0cff72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-types.c @@ -0,0 +1,664 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __unsafe_indexable + +struct unsafe_indexable_struct { + int *_Atomic __unsafe_indexable p1; + int *__unsafe_indexable _Atomic p2; + _Atomic(int *__unsafe_indexable) p3; + _Atomic(int *) __unsafe_indexable p4; +}; + +void unsafe_indexable_local(void) { + int *_Atomic __unsafe_indexable p1; + int *__unsafe_indexable _Atomic p2; + _Atomic(int *__unsafe_indexable) p3; + _Atomic(int *) __unsafe_indexable p4; + + int *_Atomic __unsafe_indexable *_Atomic __unsafe_indexable p5; + int *__unsafe_indexable _Atomic *_Atomic __unsafe_indexable p6; + _Atomic(int *__unsafe_indexable) *_Atomic __unsafe_indexable p7; + _Atomic(int *) __unsafe_indexable *_Atomic __unsafe_indexable p8; +} + +int *_Atomic __unsafe_indexable unsafe_indexable_decl_ret1(void); +int *__unsafe_indexable _Atomic unsafe_indexable_decl_ret2(void); +_Atomic(int *__unsafe_indexable) unsafe_indexable_decl_ret3(void); +_Atomic(int *) __unsafe_indexable unsafe_indexable_decl_ret4(void); + +// expected-warning@+1{{non-void function does not return a value}} +int *_Atomic __unsafe_indexable unsafe_indexable_def_ret1(void) {} +// expected-warning@+1{{non-void function does not return a value}} +int *__unsafe_indexable _Atomic unsafe_indexable_def_ret2(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *__unsafe_indexable) unsafe_indexable_def_ret3(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *) __unsafe_indexable unsafe_indexable_def_ret4(void) {} + +void unsafe_indexable_decl_p1(int *_Atomic __unsafe_indexable p); +void unsafe_indexable_decl_p2(int *__unsafe_indexable _Atomic p); +void unsafe_indexable_decl_p3(_Atomic(int *__unsafe_indexable) p); +void unsafe_indexable_decl_p4(_Atomic(int *) __unsafe_indexable p); + +void unsafe_indexable_def_p1(int *_Atomic __unsafe_indexable p) {} +void unsafe_indexable_def_p2(int *__unsafe_indexable _Atomic p) {} +void unsafe_indexable_def_p3(_Atomic(int *__unsafe_indexable) p) {} +void unsafe_indexable_def_p4(_Atomic(int *) __unsafe_indexable p) {} + +// __single + +struct single_struct { + int *_Atomic __single p1; + int *__single _Atomic p2; + _Atomic(int *__single) p3; + _Atomic(int *) __single p4; +}; + +void single_local(void) { + int *_Atomic __single p1; + int *__single _Atomic p2; + _Atomic(int *__single) p3; + _Atomic(int *) __single p4; + + int *_Atomic __single *_Atomic __single p5; + int *__single _Atomic *_Atomic __single p6; + _Atomic(int *__single) *_Atomic __single p7; + _Atomic(int *) __single *_Atomic __single p8; +} +int *_Atomic __single single_decl_ret1(void); +int *__single _Atomic single_decl_ret2(void); +_Atomic(int *__single) single_decl_ret3(void); +_Atomic(int *) __single single_decl_ret4(void); + +// expected-warning@+1{{non-void function does not return a value}} +int *_Atomic __single single_def_ret1(void) {} +// expected-warning@+1{{non-void function does not return a value}} +int *__single _Atomic single_def_ret2(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *__single) single_def_ret3(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *) __single single_def_ret4(void) {} + +void single_decl_p1(int *_Atomic __single p); +void single_decl_p2(int *__single _Atomic p); +void single_decl_p3(_Atomic(int *__single) p); +void single_decl_p4(_Atomic(int *) __single p); + +void single_def_p1(int *_Atomic __single p) {} +void single_def_p2(int *__single _Atomic p) {} +void single_def_p3(_Atomic(int *__single) p) {} +void single_def_p4(_Atomic(int *) __single p) {} + +// __indexable + +struct indexable_struct { + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable p1; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) p3; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable p4; +}; + +void indexable_local(void) { + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable p1; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) p3; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable p4; + + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable *_Atomic __indexable p9; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic *_Atomic __indexable p10; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) *_Atomic __indexable p11; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable *_Atomic __indexable p12; + + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *_Nullable __indexable) p13; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int * _Nullable) __indexable p14; +} + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *_Atomic __indexable indexable_decl_ret1(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *__indexable _Atomic indexable_decl_ret2(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *__indexable) indexable_decl_ret3(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *) __indexable indexable_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *_Atomic __indexable indexable_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *__indexable _Atomic indexable_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *__indexable) indexable_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *) __indexable indexable_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p1(int *_Atomic __indexable p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p2(int *__indexable _Atomic p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p3(_Atomic(int *__indexable) p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p4(_Atomic(int *) __indexable p); + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p1(int *_Atomic __indexable p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p2(int *__indexable _Atomic p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p3(_Atomic(int *__indexable) p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p4(_Atomic(int *) __indexable p) {} + +// __bidi_indexable + +struct bidi_indexable_struct { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) p3; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable p4; +}; + +void bidi_indexable_local(void) { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) p3; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable p4; + + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable *_Atomic __bidi_indexable p9; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic *_Atomic __bidi_indexable p10; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) *_Atomic __bidi_indexable p11; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable *_Atomic __bidi_indexable p12; +} + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *_Atomic __bidi_indexable bidi_indexable_decl_ret1(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *__bidi_indexable _Atomic bidi_indexable_decl_ret2(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *__bidi_indexable) bidi_indexable_decl_ret3(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *) __bidi_indexable bidi_indexable_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *_Atomic __bidi_indexable bidi_indexable_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *__bidi_indexable _Atomic bidi_indexable_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *__bidi_indexable) bidi_indexable_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *) __bidi_indexable bidi_indexable_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p1(int *_Atomic __bidi_indexable p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p2(int *__bidi_indexable _Atomic p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p3(_Atomic(int *__bidi_indexable) p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p4(_Atomic(int *) __bidi_indexable p); + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p1(int *_Atomic __bidi_indexable p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p2(int *__bidi_indexable _Atomic p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p3(_Atomic(int *__bidi_indexable) p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p4(_Atomic(int *) __bidi_indexable p) {} + +// __counted_by + +struct counted_by_struct { + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(16) p1; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(16)) p3; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(16) p4; +}; + +void counted_by_local(void) { + int len; + + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(len) p1; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(len) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) p3; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) p4; + + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __counted_by(len) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(len) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *_Atomic __counted_by(16) counted_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *__counted_by(16) _Atomic counted_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}}c +_Atomic(int *__counted_by(16)) counted_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *) __counted_by(16) counted_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *_Atomic __counted_by(16) counted_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *__counted_by(16) _Atomic counted_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *__counted_by(16)) counted_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *) __counted_by(16) counted_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p1(int *_Atomic __counted_by(16) p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p2(int *__counted_by(16) _Atomic p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p3(_Atomic(int *__counted_by(16)) p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p4(_Atomic(int *) __counted_by(16) p); + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p1(int *_Atomic __counted_by(16) p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p2(int *__counted_by(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p3(_Atomic(int *__counted_by(16)) p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p4(_Atomic(int *) __counted_by(16) p) {} + +// __sized_by + +struct sized_by_struct { + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *_Atomic __sized_by(16) p1; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *__sized_by(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(16)) p3; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(16) p4; +}; + +void sized_by_local(void) { + int size; + + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *_Atomic __sized_by(size) p1; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *__sized_by(size) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(size)) p3; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(size) p4; + + // expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __sized_by(size) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__sized_by(size) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(size)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(size) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *_Atomic __sized_by(16) sized_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *__sized_by(16) _Atomic sized_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *__sized_by(16)) sized_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *) __sized_by(16) sized_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *_Atomic __sized_by(16) sized_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *__sized_by(16) _Atomic sized_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *__sized_by(16)) sized_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *) __sized_by(16) sized_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p1(int *_Atomic __sized_by(16) p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p2(int *__sized_by(16) _Atomic p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p3(_Atomic(int *__sized_by(16)) p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p4(_Atomic(int *) __sized_by(16) p); + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p1(int *_Atomic __sized_by(16) p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p2(int *__sized_by(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p3(_Atomic(int *__sized_by(16)) p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p4(_Atomic(int *) __sized_by(16) p) {} + +// __counted_by_or_null + +struct counted_by_or_null_struct { + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *_Atomic __counted_by_or_null(16) p1; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *__counted_by_or_null(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(16)) p3; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(16) p4; +}; + +void counted_by_or_null_local(void) { + int len; + + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *_Atomic __counted_by_or_null(len) p1; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *__counted_by_or_null(len) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(len)) p3; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(len) p4; + + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __counted_by_or_null(len) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by_or_null(len) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(len)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(len) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *_Atomic __counted_by_or_null(16) counted_by_or_null_decl_ret1(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *__counted_by_or_null(16) _Atomic counted_by_or_null_decl_ret2(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}}c +_Atomic(int *__counted_by_or_null(16)) counted_by_or_null_decl_ret3(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *) __counted_by_or_null(16) counted_by_or_null_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *_Atomic __counted_by_or_null(16) counted_by_or_null_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *__counted_by_or_null(16) _Atomic counted_by_or_null_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *__counted_by_or_null(16)) counted_by_or_null_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *) __counted_by_or_null(16) counted_by_or_null_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p1(int *_Atomic __counted_by_or_null(16) p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p2(int *__counted_by_or_null(16) _Atomic p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p3(_Atomic(int *__counted_by_or_null(16)) p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p4(_Atomic(int *) __counted_by_or_null(16) p); + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p1(int *_Atomic __counted_by_or_null(16) p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p2(int *__counted_by_or_null(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p3(_Atomic(int *__counted_by_or_null(16)) p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p4(_Atomic(int *) __counted_by_or_null(16) p) {} + +// __sized_by_or_null + +struct sized_by_or_null_struct { + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *_Atomic __sized_by_or_null(16) p1; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *__sized_by_or_null(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(16)) p3; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(16) p4; +}; + +void sized_by_or_null_local(void) { + int size; + + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *_Atomic __sized_by_or_null(size) p1; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *__sized_by_or_null(size) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(size)) p3; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(size) p4; + + // expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __sized_by_or_null(size) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__sized_by_or_null(size) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(size)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(size) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *_Atomic __sized_by_or_null(16) sized_by_or_null_decl_ret1(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *__sized_by_or_null(16) _Atomic sized_by_or_null_decl_ret2(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *__sized_by_or_null(16)) sized_by_or_null_decl_ret3(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *) __sized_by_or_null(16) sized_by_or_null_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *_Atomic __sized_by_or_null(16) sized_by_or_null_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *__sized_by_or_null(16) _Atomic sized_by_or_null_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *__sized_by_or_null(16)) sized_by_or_null_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *) __sized_by_or_null(16) sized_by_or_null_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p1(int *_Atomic __sized_by_or_null(16) p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p2(int *__sized_by_or_null(16) _Atomic p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p3(_Atomic(int *__sized_by_or_null(16)) p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p4(_Atomic(int *) __sized_by_or_null(16) p); + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p1(int *_Atomic __sized_by_or_null(16) p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p2(int *__sized_by_or_null(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p3(_Atomic(int *__sized_by_or_null(16)) p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p4(_Atomic(int *) __sized_by_or_null(16) p) {} + +// __ended_by + +void ended_by_params( + int * pEnd, + + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *_Atomic __ended_by(pEnd) p1, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *__ended_by(pEnd) _Atomic p2, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *__ended_by(pEnd)) p3, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *) __ended_by(pEnd) p4, + + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *_Atomic __ended_by(pEnd) * _Atomic __unsafe_indexable p5, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *__ended_by(pEnd) _Atomic *_Atomic __unsafe_indexable p6, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *__ended_by(pEnd)) *_Atomic __unsafe_indexable p7, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *) __ended_by(pEnd) * _Atomic __unsafe_indexable p8, + + int *_Atomic p9, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p9) pStart, + int *_Atomic * _Atomic p10, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p10) pStart2, + int * * _Atomic p11, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p11) pStart3, + int * _Atomic * p12, + int * * __ended_by(p12) pStart4 +); + +// __terminated_by + +struct terminated_by_struct { + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single p1; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic p2; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) p3; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single p4; +}; + +void terminated_by_local(void) { + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single p1; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic p2; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) p3; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single p4; + + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single *_Atomic __null_terminated __single p9; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic *_Atomic __null_terminated __single p10; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) *_Atomic __null_terminated __single p11; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single *_Atomic __null_terminated __single p12; +} + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *_Atomic __null_terminated __single terminated_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *__null_terminated __single _Atomic terminated_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *__null_terminated __single) terminated_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *) __null_terminated __single terminated_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *_Atomic __null_terminated __single terminated_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *__null_terminated __single _Atomic terminated_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *__null_terminated __single) terminated_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *) __null_terminated __single terminated_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p1(int *_Atomic __null_terminated __single p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p2(int *__null_terminated __single _Atomic p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p3(_Atomic(int *__null_terminated __single) p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p4(_Atomic(int *) __null_terminated __single p); + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p1(int *_Atomic __null_terminated __single p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p2(int *__null_terminated __single _Atomic p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p3(_Atomic(int *__null_terminated __single) p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p4(_Atomic(int *) __null_terminated __single p) {} diff --git a/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c b/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c new file mode 100644 index 0000000000000..f6951dffca943 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +// pass_dynamic_object_size attribute only applies to constant pointer +// arguments, make sure that after handling __null_terminated the pointer +// remains const. + +unsigned long my_strlen_c(const char *const s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} + +unsigned long my_strlen_c_nt(const char *const __null_terminated s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} + +unsigned long my_strlen_nt_c(const char *__null_terminated const s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} diff --git a/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c b/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c new file mode 100644 index 0000000000000..08e70d1330d33 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wuninitialized -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wuninitialized -verify %s + +inline void *a() { + void *b; // expected-note{{initialize the variable 'b' to silence this warning}} + return b; // expected-warning{{variable 'b' is uninitialized when used here}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c b/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c new file mode 100644 index 0000000000000..3fa4c92f74af4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(int *__counted_by(len) ptr, int len) { + __auto_type p = ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c b/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c new file mode 100644 index 0000000000000..f86ed0db4ecc2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c @@ -0,0 +1,115 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct SequencePtrs { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void TestEndedBy(struct SequencePtrs *sp) { + __auto_type local_start = sp->start; // expected-error{{passing '__ended_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_iter = sp->iter; // expected-error{{passing '__ended_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_end = sp->end; // expected-error{{passing end pointer as __auto_type initializer is not yet supported}} +} + +void TestCountedBy(int *__counted_by(len) ptr, int len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestSizedBy(void *__sized_by(len) ptr, unsigned len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__sized_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestOutCountedBy(int *__counted_by(*len) *ptr, int *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void TestOutSizedBy(void *__sized_by(*len) *ptr, unsigned *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__sized_by' pointer as __auto_type initializer is not yet supported}} +} + +void TestCountedByOrNull(int *__counted_by_or_null(len) ptr, int len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__counted_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestSizedByOrNull(void *__sized_by_or_null(len) ptr, unsigned len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__sized_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestOutCountedByOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void TestOutSizedByOrNull(void *__sized_by_or_null(*len) *ptr, unsigned *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__sized_by_or_null' pointer as __auto_type initializer is not yet supported}} +} + +void TestOutCountedByArray(int (*ptr)[__counted_by(*len)], int *len) { // expected-error{{pointer to incomplete __counted_by array type 'int[]' not allowed; did you mean to use a nested pointer type?}} + __auto_type local_counted_ptr = ptr; // expected-error{{array with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{array with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + + +struct CountedStruct { + char * __counted_by(n) buf; + int n; +}; + +void foo(struct CountedStruct * __single p) { + __auto_type ebuf = p->buf; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type *__single eptr = &(p->buf); // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void baz(void * __unsafe_indexable p); + +void bar(char * p) { + char * implicit_bidi = p; + baz( + (void*)({ + __auto_type pointer_to_bidi = &implicit_bidi; + *pointer_to_bidi; + }) + ); +} + +struct FAMStruct { + int n; + char buf[__counted_by(n)]; +}; + +void foo_fam(struct FAMStruct * __single p) { + __auto_type ebuf = p->buf; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type *__single eptr = &(p->buf); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(n)]'}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c b/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c new file mode 100644 index 0000000000000..b32a6ce18ed45 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// expected-no-diagnostics + +#include + +typedef struct foo { + int field; +} *__single foo_t; + +extern void takes_foo(foo_t foo); + +struct foo array_of_one[1]; + +void +call_takes_foo(void) +{ + __auto_type foo = array_of_one; + takes_foo(foo); +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c new file mode 100644 index 0000000000000..f9d1c22eeb73f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c @@ -0,0 +1,46 @@ + + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int *__counted_by(n) cb_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__counted_by(*n) cb_out(int *n) { + int **n1 = &n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *n2 = n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n3 = *n; +} + +void *__sized_by(n) sb_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__counted_by_or_null(n) cbn_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +void *__sized_by_or_null(n) sbn_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__ended_by(end) eb_in(int *end) { + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +int *__ended_by(*end) eb_out(int **end) { + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c new file mode 100644 index 0000000000000..5bec5530cdd98 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c @@ -0,0 +1,143 @@ + + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s + +#include + +void i(int); +void pi(int *); +void ppi(int **); +void pppi(int ***); + +// __counted_by() + +void cb_in_in(int count, int *__counted_by(count) ptr); +void cb_in_out(int count, int *__counted_by(count) *ptr); +void cb_out_in(int *count, int *__counted_by(*count) ptr); +void cb_out_out(int *count, int *__counted_by(*count) *ptr); + +int *__counted_by(count) ret_cb_in(int count); +int *__counted_by(*count) ret_cb_out(int *count); + +int *__counted_by(count) mixed_simple(int count, int *__counted_by(count) ptr); +int *__counted_by(*count) mixed_inout_count(int *count, int *__counted_by(*count) ptr); +int *__counted_by(*count) mixed_out_ptr(int *count, int *__counted_by(*count) *ptr); +int *__counted_by(*count) mixed_inout_count_out_ptr(int *count, int *__counted_by(*count) ptr, int *__counted_by(*count) *out_ptr); + +int *__counted_by(count) test_cb_in(int count) { + int c; + int *__counted_by(c) p; + + count = 42; // rs-error{{parameter 'count' is implicitly read-only due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + i(count); + pi(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + cb_in_in(count, p); + cb_in_out(count, &p); // expected-error{{passing address of 'p' as an indirect parameter; must also pass 'c' or its address because the type of 'p', 'int *__single __counted_by(c)' (aka 'int *__single'), refers to 'c'}} + cb_out_in(&count, p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + cb_out_out(&count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + (void)ret_cb_in(count); + (void)ret_cb_out(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + (void)mixed_simple(count, p); + (void)mixed_inout_count(&count, p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_out_ptr(&count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_inout_count_out_ptr(&count, p, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} +} + +int *__counted_by(*count) test_cb_out(int *count) { + int c; + int *__counted_by(c) p; + + *count = 42; + count = p; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter or return type}} + + i(*count); + pi(count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + ppi(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + + cb_in_in(*count, p); + cb_in_out(*count, &p); // expected-error{{passing address of 'p' as an indirect parameter; must also pass 'c' or its address because the type of 'p', 'int *__single __counted_by(c)' (aka 'int *__single'), refers to 'c'}} + cb_out_in(count, p); + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + cb_out_out(count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + + (void)ret_cb_in(*count); + (void)ret_cb_out(count); + + (void)mixed_simple(*count, p); + (void)mixed_inout_count(count, p); + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_out_ptr(count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_inout_count_out_ptr(count, p, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} +} + +// Other variants of __counted_by() +// The logic is the same as for __counted_by(), so test only a few cases. + +int *__counted_by_or_null(count) test_cbn_in(int count) { + count = 42; // rs-error{{parameter 'count' is implicitly read-only due to being used by the '__counted_by_or_null' attribute in the return type of 'test_cbn_in' ('int *__single __counted_by_or_null(count)' (aka 'int *__single'))}} +} + +void *__sized_by(size) test_sb_in(int size) { + size = 42; // rs-error{{parameter 'size' is implicitly read-only due to being used by the '__sized_by' attribute in the return type of 'test_sb_in' ('void *__single __sized_by(size)' (aka 'void *__single'))}} +} + +void *__sized_by_or_null(size) test_sbn_in(int size) { + size = 42; // rs-error{{parameter 'size' is implicitly read-only due to being used by the '__sized_by_or_null' attribute in the return type of 'test_sbn_in' ('void *__single __sized_by_or_null(size)' (aka 'void *__single'))}} +} + +// __ended_by() + +void eb_in(int *__ended_by(end) start, int *end); +void eb_out(int *__ended_by(*end) start, int **end); + +int *__ended_by(end) ret_eb_in(int *end); +int *__ended_by(*end) ret_eb_out(int **end); + +int *__ended_by(end) test_eb_in(int *end) { + int *p; + + *end = 42; + end = p; // rs-error{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + + i(*end); + pi(end); + ppi(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + + eb_in(p, end); + eb_out(p, &end); // expected-error{{type of 'end', 'int *__single', is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + + (void)ret_eb_in(end); + (void)ret_eb_out(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} +} + +int *__ended_by(*end) test_eb_out(int **end) { + int *__single p; + + **end = 42; + *end = p; + end = &p; // rs-error{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + + i(**end); + pi(*end); + ppi(end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + pppi(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + + eb_in(p, *end); + eb_out(p, end); // expected-error{{type of 'end', 'int *__single*__single', is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + + (void)ret_eb_in(*end); + (void)ret_eb_out(end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c new file mode 100644 index 0000000000000..a6a25e979081d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c @@ -0,0 +1,27 @@ + + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +// The warnings should be always errors (rdar://136173954). + +// expected-warning@+1{{non-void function does not return a value}} +int *__counted_by(42) missing(void) {} + +// expected-warning@+1{{non-void function does not return a value}} +int *__counted_by(count) missing2(int count) {} + +int *__counted_by(42) mismatch(void) { + return; // expected-error{{non-void function 'mismatch' should return a value}} +} + +int *__counted_by(count) mismatch2(int count) { + return; // expected-error{{non-void function 'mismatch2' should return a value}} +} diff --git a/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c b/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c new file mode 100644 index 0000000000000..2c0e5c6c4a211 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -x c -verify -verify=c %s +// RUN: %clang_cc1 -x c++ -verify=expected -verify=cxx %s +// RUN: %clang_cc1 -x objective-c -verify -verify=c %s +// RUN: %clang_cc1 -x objective-c++ -verify=expected -verify=cxx %s + +int *__attribute__((bidi_indexable)) gbi; // expected-warning{{attribute ignored}} +int *__attribute__((unsafe_indexable)) gus; // expected-warning{{attribute ignored}} + +struct S1 { + // FIXME: c++ drops counted_by entirely because of late parsing but that's okay for now. + int *__attribute__((counted_by(len))) ptr; // expected-error{{undeclared identifier 'len'}} + int len; +}; + +struct S2 { + int len; + // cxx-error@+1{{invalid use of non-static data member 'len'}} + int *__attribute__((counted_by(len))) ptr; // ok +}; + +int foo1(int *__attribute__((counted_by(len))) ptr, int len) { // expected-error{{use of undeclared identifier 'len'}} + int i; + // cxx-error@+2{{cannot initialize a variable of type}} + // c-error@+1{{incompatible integer to pointer conversion initializing}} + int *__attribute__((indexable)) pi = i; // expected-warning{{attribute ignored}} + int *__attribute__((single)) ps; // expected-warning{{attribute ignored}} + return 0; +} + +// cxx-warning@+1{{'counted_by' attribute ignored}} +int foo2(int len, int *__attribute__((counted_by(len))) ptr); // c-error{{counted_by attribute only applies to non-static data members}} + +// c-error@+2{{sized_by attribute only applies to non-static data members}} +// cxx-warning@+1{{'sized_by' attribute ignored}} +void bar1(int size, void *__attribute__((sized_by(size))) ptr); +void bar2(void *__attribute__((sized_by(size))) ptr, int size); // expected-error{{use of undeclared identifier 'size'}} + +void baz1(void *end, void *__attribute__((ended_by(end))) start); // expected-warning{{'ended_by' attribute ignored}} +void baz2(void *__attribute__((ended_by(end))) start, void *end); // expected-error{{use of undeclared identifier 'end'}} diff --git a/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c b/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c new file mode 100644 index 0000000000000..49aa074a580ff --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#include +#include + +#ifndef __bidi_indexable +#define __bidi_indexable +#endif + +typedef struct { + uint8_t dummy; + uint32_t value; +} __attribute__((packed)) packed_t; + +void external(void * __sized_by(size), int size); + +void test(void) { + packed_t s; + const int size = 4; + external(&s.value, size); + void *local = &s.value; +} + +void test_explicit_cast_single(void) { + packed_t s; + const int size = 4; + external((uint32_t * __single) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __bidi_indexable)(uint32_t * __single) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} +} + +void test_explicit_cast_bidi(void) { + packed_t s; + const int size = 4; + external((uint32_t * __bidi_indexable) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __bidi_indexable) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} +} + +void test_explicit_cast_sized(void) { + packed_t s; + const int size = 4; + external((uint32_t * __sized_by(size)) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __sized_by(size)) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + external((void * __sized_by(size)) &s.value, size); + local = (void * __sized_by(size)) &s.value; +} diff --git a/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..f7efe3649a2e8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{consider adding '__sized_by(10)' to 'dst'}} +void foo(char *dst, char *src) { + // expected-error@+1{{passing 'char *__single' with pointee of size 1 to parameter of type 'void *__single __sized_by()' (aka 'void *__single') with size value of 10 always fails}} + __builtin_memcpy(dst, src, 10); +} diff --git a/clang/test/BoundsSafety/Sema/check-format-arguments.c b/clang/test/BoundsSafety/Sema/check-format-arguments.c new file mode 100644 index 0000000000000..15b78af96066e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/check-format-arguments.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify -Wformat-nonliteral %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify -Wformat-nonliteral %s + +#include +#include + +#define __printflike(__fmt,__varargs) __attribute__((__format__ (__printf__, __fmt, __varargs))) + +void __printflike(1, 0) foo(const char *__null_terminated, va_list); + +void __printflike(2, 3) bar(const char *__unsafe_indexable p1, const char *__unsafe_indexable p2, ...) { + va_list variadicArgs; + va_start(variadicArgs, p2); + + foo(__unsafe_forge_null_terminated(const char *, p2), variadicArgs); + foo(__unsafe_forge_null_terminated(const char *, p2+1), variadicArgs); + foo(__unsafe_forge_null_terminated(const char *, "Hello, %s!\n"), variadicArgs); + + foo(__unsafe_forge_null_terminated(const char *, 2), variadicArgs); // expected-warning{{format string is not a string literal}} + foo(__unsafe_forge_null_terminated(const char *, p1), variadicArgs); // expected-warning{{format string is not a string literal}} + + va_end(variadicArgs); +} diff --git a/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c new file mode 100644 index 0000000000000..2ee638bd46b95 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// Tests the correctness of applying bounds attributes to complex type specifiers +// as well as to what extent other attributes (represented by _Nullable) are retained. + +#include "complex-typespecs-with-bounds.h" +#include + +void typeoftypes() { + typeof((long * _Nullable) 0) __single p1; + typeof(typeof(bar) *) __single p2; +} + +struct S { + // expected-note@+1 2{{pointer 'S::f1' declared here}} + char * _Nullable f1; +}; + +void typeofexprs(struct S s) { + // expected-error@+1{{'__single' attribute only applies to pointer arguments}} + typeof(foo) __single p1; + // expected-error@+1{{initializing 'typeof (foo())' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(foo()) __single p2 = foo(); + typeof(&foo) __single p3 = &foo; + // expected-error@+1{{function pointers cannot be indexable}} + typeof(&foo) __bidi_indexable p4; + typeof(&foo) __unsafe_indexable p5 = &foo; + + // expected-error@+1{{initializing 'typeof (bar)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(bar) __single p6 = bar; + typeof(&bar) __single p7 = &bar; + typeof(bar) * __single p8 = &bar; + // expected-error@+1{{initializing 'typeof (bar[2]) *__single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(bar[2]) * __single p9 = bar; + // expected-error@+1{{initializing 'typeof (&bar[2])' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(&bar[2]) __single p10 = bar; + // expected-error@+1{{initializing 'typeof (&*bar)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(&*bar) __single p11 = &*bar; + + // expected-warning@+1{{initializing type 'typeof (s.f1)' (aka 'char *__bidi_indexable') with an expression of type 'char *__single _Nullable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'S::f1'}} + typeof(s.f1) __bidi_indexable p12 = s.f1; + // expected-warning@+1{{initializing type 'typeof (*s.f1) *__bidi_indexable' (aka 'char *__bidi_indexable') with an expression of type 'char *__single _Nullable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'S::f1'}} + typeof(*s.f1) * __bidi_indexable p13 = s.f1; + typeof(&*s.f1) __unsafe_indexable p14 = s.f1; +} + +typedef typeof(*bar) my_t; +typedef typeof(bar) my_ptr_t; +typedef typeof(*bar) * my_manual_ptr_t; + +void typedefs_of_typeof() { + // expected-error@+1{{initializing 'my_t *__single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_t * __single p1 = bar; + // expected-error@+1{{initializing 'my_ptr_t __single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_ptr_t __single p2 = bar; + // expected-error@+1{{initializing 'my_manual_ptr_t __single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_manual_ptr_t __single p3 = bar; + // expected-error@+1{{initializing 'my_manual_ptr_t __bidi_indexable' (aka 'char *__bidi_indexable') with an expression of incompatible type 'char *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_manual_ptr_t __bidi_indexable p4 = bar; + my_manual_ptr_t __unsafe_indexable p5 = bar; + // expected-error@+1{{assigning to 'my_manual_ptr_t __bidi_indexable' (aka 'char *__bidi_indexable') from incompatible type 'my_manual_ptr_t __unsafe_indexable' (aka 'char *__unsafe_indexable') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + p4 = p5; +} + +void autotypes(void * void_param) { + // this could probably be made to work in theory, but it can always be worked around by simply adding a '*' + __auto_type __single p1 = bar; // expected-error{{bounds attribute '__single' cannot be applied to an undeduced type}} + __auto_type * __single p2 = bar; // expected-error{{initializing 'char *__single' with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + __auto_type * __single p3 = &*bar; // expected-error{{initializing 'char *__single' with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + + // check that an undeduced void pointee doesn't get around errors regarding type size + __auto_type * __bidi_indexable p4 = void_ptr; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'void *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-note@+1{{pointer 'p5' declared here}} + __auto_type * __bidi_indexable p5 = void_param; // expected-error{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'p5' as '__single'}} +} + +void typeofexpr_typeofexpr() { + typeof(bar) p1; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeofexpr_typeoftype_typeofexpr() { + typeof(typeof(bar)) p1; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeof_autotype1() { + __auto_type p1 = bar; + // expected-error@+1{{bounds attribute '__single' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __single p2 = bar; + // expected-error@+1{{bounds attribute '__bidi_indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __bidi_indexable p3 = bar; + // expected-error@+1{{bounds attribute '__unsafe_indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __unsafe_indexable p4 = bar; + // expected-error@+1{{bounds attribute '__indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __indexable p5 = bar; +} + +void typeof_autotype2() { + __auto_type * p1 = bar; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeof_autotype3() { + __auto_type p1 = bare; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bare; +} + +// check that we don't emit the same error twice +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int * _Nullable) __attribute__((address_space(2))) __indexable global1; +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int * _Atomic __attribute__((address_space(2))) __indexable global2; diff --git a/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h new file mode 100644 index 0000000000000..11d3afb2a1003 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h @@ -0,0 +1,8 @@ +#pragma clang system_header +// This is a system header for historical reasons that no longer apply. +// It still silences unrelated warnings in this header, so I'm leaving it. + +char * _Nullable foo(); +char * _Nullable bar; +char * bare; +void * void_ptr; diff --git a/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c b/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c new file mode 100644 index 0000000000000..70c76337697d4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c @@ -0,0 +1,435 @@ +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +void consume_cb_with_other_data(struct cb_with_other_data); + +struct NestedCB { + struct cb_with_other_data inner; + int count; + char* __counted_by(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_cb_with_other_data((struct cb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct cb_with_other_data global_no_diags = (struct cb_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + + +struct cb_with_other_data field_initializers_with_side_effects(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct cb_with_other_data s2 = (struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_cb_with_other_data((struct cb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct cb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_cb_with_other_data { + struct cb_with_other_data s; + int other; + }; + (void)(struct Contains_cb_with_other_data) { + .s = { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedCB) { + // expected-warning@+1{{initializer (struct cb_with_other_data){.count = 0, .buf = 0, .other = side_effect()} has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct cb_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct cb_with_other_data[]){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_cb_with_other_data { + struct cb_with_other_data u; + int other; + }; + + (void)(union UnionWith_cb_with_other_data) { + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct cb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct cb_with_other_data) { 0, 0x0, side_effect()}; +} + +struct cb_with_other_data global_cb_with_other_data_side_effect_init = + (struct cb_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct cb_with_other_data side_effects_in_ptr(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct cb_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct cb_with_other_data side_effects_in_count(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedCB) { + .inner = (struct cb_with_other_data) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct cb_with_other_data ){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + -1, + // expected-error@+1{{negative count value of -1 for 'char *__single __counted_by(count)' (aka 'char *__single')}} + 0x0, + 0x0 + }; +} + +struct cb_with_other_data global_cb_with_other_data_constant_neg_count = + (struct cb_with_other_data) { + // expected-error@+1{{negative count value of -1 for 'global_cb_with_other_data_constant_neg_count.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .count = -1, + .other = 0x0 +}; + +struct NestedCB global_nested_cb_constant_neg_count = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = -1, + // expected-error@+1{{negative count value of -1 for 'global_nested_cb_constant_neg_count.inner.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0x0 + }, + .count = -1, + // expected-error@+1{{negative count value of -1 for 'global_nested_cb_constant_neg_count.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0 +}; + +void constant_pos_count_nullptr(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + 0x0, + 0x0 + }; +} + +struct cb_with_other_data global_cb_with_other_data_constant_pos_count_nullptr = + (struct cb_with_other_data) { + // expected-error@+1{{initializing 'global_cb_with_other_data_constant_pos_count_nullptr.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .count = 100, + .other = 0x0 +}; + +struct NestedCB global_nested_cb_constant_post_count_nullptr = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = 100, + // expected-error@+1{{initializing 'global_nested_cb_constant_post_count_nullptr.inner.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .other = 0x0 + }, + .count = 100, + // expected-error@+1{{initializing 'global_nested_cb_constant_post_count_nullptr.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .other = 0 +}; + +void bad_arr_size(struct cb_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with array 'arr' (which has 3 elements) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct cb_with_other_data global_cb_with_other_data_bad_arr_size = + (struct cb_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct cb_with_other_data* s, char* ptr) { // expected-note{{consider adding '__counted_by(100)' to 'ptr'}} + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with 'char *__single' always fails}} + ptr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char* global_ptr; +struct cb_with_other_data global_cb_with_other_data_bad_count_single_ptr = + (struct cb_with_other_data) { + .buf = global_ptr , // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_implicit_zero_count(struct cb_with_other_data* s, char*__bidi_indexable ptr, struct NestedCB* ncb) { + *s = (struct cb_with_other_data){ + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + }; + + *ncb = (struct NestedCB) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct cb_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + } + }; +} + +void unknown_count_and_single_ptr_src(struct cb_with_other_data* s, char* ptr, struct NestedCB* ncb) { + int count = get_count(); + *s = (struct cb_with_other_data){ + count, // expected-note{{count initialized here}} + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + ptr, + 0x0 + }; + + *ncb = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }, + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct cb_with_other_data[]' with non-constant array of type 'struct cb_with_other_data[2]'}} + struct cb_with_other_data arr[] = (struct cb_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer get_count() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_cb(int new_count) { + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c b/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c new file mode 100644 index 0000000000000..42b7ac49e9ae0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c @@ -0,0 +1,416 @@ +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +void consume_cbon_with_other_data(struct cbon_with_other_data); + +struct NestedCBON { + struct cbon_with_other_data inner; + int count; + char* __counted_by_or_null(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct cbon_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct cbon_with_other_data global_no_diags = (struct cbon_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + +struct cbon_with_other_data field_initializers_with_side_effects(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct cbon_with_other_data s2 = (struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_cbon_with_other_data((struct cbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct cbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_cbon_with_other_data { + struct cbon_with_other_data s; + int other; + }; + (void)(struct Contains_cbon_with_other_data) { + .s = { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedCBON) { + // expected-warning@+1{{initializer (struct cbon_with_other_data){.count = 0, .buf = 0, .other = side_effect()} has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct cbon_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct cbon_with_other_data[]){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_cbon_with_other_data { + struct cbon_with_other_data u; + int other; + }; + + (void)(union UnionWith_cbon_with_other_data) { + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct cbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct cbon_with_other_data) { 0, 0x0, side_effect()}; +} + +struct cbon_with_other_data global_cbon_with_other_data_side_effect_init = + (struct cbon_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct cbon_with_other_data side_effects_in_ptr(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct cbon_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct cbon_with_other_data side_effects_in_count(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct cbon_with_other_data ){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + -1, + 0x0, // ok + 0x0 + }; +} + +struct cbon_with_other_data global_cbon_with_other_data_constant_neg_count = + (struct cbon_with_other_data) { + .buf = 0x0, // ok + .count = -1, + .other = 0x0 +}; + +struct NestedCBON global_nested_cbon_constant_neg_count = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = -1, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = -1, + .buf = 0x0, + .other = 0 +}; + +void constant_pos_count_nullptr(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 100, + 0x0, // OK + 0x0 + }; +} + +struct cbon_with_other_data global_cbon_with_other_data_constant_pos_count_nullptr = + (struct cbon_with_other_data) { + .buf = 0x0, // ok + .count = 100, + .other = 0x0 +}; + +struct NestedCBON global_nested_cbon_constant_post_count_nullptr = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = 100, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = 100, + .buf = 0x0, // OK + .other = 0 +}; + +void bad_arr_size(struct cbon_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct cbon_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and count value of 100 with array 'arr' (which has 3 elements) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct cbon_with_other_data global_cbon_with_other_data_bad_arr_size = + (struct cbon_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct cbon_with_other_data* s, char* ptr) { + *s = (struct cbon_with_other_data){ + 100, + // No diag because `ptr` might be null + ptr, + 0x0 + }; +} + +void bad_implicit_zero_count(struct cbon_with_other_data* s, char*__bidi_indexable ptr, struct NestedCBON* ncbon) { + *s = (struct cbon_with_other_data){ + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + }; + + *ncbon = (struct NestedCBON) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct cbon_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + } + }; +} + +void unknown_count_and_single_ptr_src(struct cbon_with_other_data* s, char* ptr, struct NestedCBON* ncbon) { + int count = get_count(); + *s = (struct cbon_with_other_data){ + count, // expected-note{{count initialized here}} + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + ptr, + 0x0 + }; + + *ncbon = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }, + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct cbon_with_other_data[]' with non-constant array of type 'struct cbon_with_other_data[2]'}} + struct cbon_with_other_data arr[] = (struct cbon_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer get_count() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_cbon(int new_count) { + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c b/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c new file mode 100644 index 0000000000000..274a2c6afd4cd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c @@ -0,0 +1,323 @@ +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +void consume_eb_with_other_data(struct eb_with_other_data); + +struct NestedEB { + struct eb_with_other_data inner; + char* end; + char* __ended_by(end) start; + int other; +}; + +struct no_attr_with_other_data { + char* end; + char* start; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct eb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_eb_with_other_data((struct eb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct eb_with_other_data global_no_diags = (struct eb_with_other_data) { + // FIXME: This should be allowed. rdar://81135826 + 0x0, // both-error{{initializer element is not a compile-time constant}} + 0x0, + 0x0 +}; + +struct eb_with_other_data field_initializers_with_side_effects(struct eb_with_other_data* s) { + *s = (struct eb_with_other_data){ + 0x0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct eb_with_other_data s2 = (struct eb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_eb_with_other_data((struct eb_with_other_data){ + 0x0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_eb_with_other_data((struct eb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0} + ); + + (void) (struct eb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0}; + + // no diags for structs without attributes + struct no_attrs { + char* start; + char* end; + int other; + }; + (void) (struct no_attrs) { 0x0, 0x0, side_effect()}; + + // Nested + struct Contains_eb_with_other_data { + struct eb_with_other_data s; + int other; + }; + (void)(struct Contains_eb_with_other_data) { + .s = { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedEB) { + // expected-warning@+1{{initializer (struct eb_with_other_data){.start = 0, .end = 0, .other = side_effect()} has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct eb_with_other_data) { + .start = 0x0, + .end = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .start = 0x0, + .end = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct eb_with_other_data[]){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0x0, 0x0, side_effect()}, + {0x0, 0x0, 0x0} + }; + + union UnionWith_eb_with_other_data { + struct eb_with_other_data u; + int other; + }; + + (void)(union UnionWith_eb_with_other_data) { + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct eb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + ); + + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct eb_with_other_data) { 0x0, 0x0, side_effect()}; +}; + +struct eb_with_other_data global_eb_with_other_data_side_effect_init = + (struct eb_with_other_data) { + // FIXME: Location of first non-constant is wrong (rdar://81135826) + .start = 0x0, // both-error{{initializer element is not a compile-time constant}} + .end = 0x0, + .other = side_effect() +}; + +struct eb_with_other_data side_effects_in_ptrs(struct eb_with_other_data* s) { + *s = (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; + + consume_eb_with_other_data( + (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + } + ); + + (void)(struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; + + (void)(struct NestedEB) { + .inner = (struct eb_with_other_data) { + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + .start = get_single_ptr(), + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + .end = get_single_ptr(), + .other = 0 + }, + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + .end = get_single_ptr(), + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + .start = get_single_ptr(), + .other = 0 + }; + + return (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void partial_init_start(struct eb_with_other_data* s, char* start, struct NestedEB* neb) { + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = start }; + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .start = start + },// expected-warning{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .start = start + }; // expected-warning{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} +} + + +// Partial init diagnostic isn't emitted because `global_start_ptr` isn't constant +char* global_start_ptr; +struct eb_with_other_data global_eb_partial_init = + (struct eb_with_other_data) { + .other = 0x0, + .start = global_start_ptr // both-error{{initializer element is not a compile-time constant}} +}; +struct eb_with_other_data global_eb_partial_init2 = + (struct eb_with_other_data) { + .other = 0x0, + // FIXME: This should be treated as a constant (rdar://81135826) + .start = __unsafe_forge_bidi_indexable(char*, 0x1, 4) // both-error{{initializer element is not a compile-time constant}} +}; + + +void end_is_null(struct eb_with_other_data* s, char* start, struct NestedEB* neb) { + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = start, .end = 0x0 }; + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .start = start, + // expected-warning@+1{{nitializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .end = 0x0 + }, + .start = start, + // expected-warning@+1{{nitializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .end = 0x0 + }; +} + +void partial_init_ended(struct eb_with_other_data* s, char* end, struct NestedEB* neb) { + // expected-warning@+1{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .end = end }; + + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .end = end + }, // expected-warning{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + .end = end + }; // expected-warning{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} +} + +void start_is_null(struct eb_with_other_data* s, char* end) { + // expected-warning@+1{{initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = 0x0, .end = end}; +} + + +void partial_init_other(struct eb_with_other_data* s, struct NestedEB* neb) { + // OK + *s = (struct eb_with_other_data){ .other = 0x0 }; + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) {.other = 0x0}, + .other = 0x0 + }; +} + +void implicit_init_all(struct eb_with_other_data* s, struct NestedEB* neb) { + // OK + *s = (struct eb_with_other_data){0}; + *neb = (struct NestedEB) {0}; + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) {0} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct eb_with_other_data[]' with non-constant array of type 'struct eb_with_other_data[2]'}} + struct eb_with_other_data arr[] = (struct eb_with_other_data[]){ + {.start = ptr, .end = ptr + 1, .other = 0x0}, + // expected-warning@+1{{initializer get_count() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.start = ptr, .end = ptr + 1, .other = get_count()}, + }; +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c b/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c new file mode 100644 index 0000000000000..31885296212be --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c @@ -0,0 +1,431 @@ +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +void consume_sb_with_other_data(struct sb_with_other_data); + +struct NestedSB { + struct sb_with_other_data inner; + int count; + char* __sized_by(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct sb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_sb_with_other_data((struct sb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct sb_with_other_data global_no_diags = (struct sb_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + + +struct sb_with_other_data field_initializers_with_side_effects(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct sb_with_other_data s2 = (struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_sb_with_other_data((struct sb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct sb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_sb_with_other_data { + struct sb_with_other_data s; + int other; + }; + (void)(struct Contains_sb_with_other_data) { + .s = { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedSB) { + // expected-warning@+1{{initializer (struct sb_with_other_data){.count = 0, .buf = 0, .other = side_effect()} has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct sb_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct sb_with_other_data[]){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_sb_with_other_data { + struct sb_with_other_data u; + int other; + }; + + (void)(union UnionWith_sb_with_other_data) { + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct sb_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct sb_with_other_data) { 0, 0x0, side_effect()}; +} + +struct sb_with_other_data global_sb_with_other_data_side_effect_init = + (struct sb_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct sb_with_other_data side_effects_in_ptr(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct sb_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct sb_with_other_data side_effects_in_count(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedSB) { + .inner = (struct sb_with_other_data) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct sb_with_other_data ){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + -1, + // expected-error@+1{{negative size value of -1 for 'char *__single __sized_by(count)' (aka 'char *__single')}} + 0x0, + 0x0 + }; +} + +struct sb_with_other_data global_sb_with_other_data_constant_neg_count = + (struct sb_with_other_data) { + // expected-error@+1{{negative size value of -1 for 'global_sb_with_other_data_constant_neg_count.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .count = -1, + .other = 0x0 +}; + +struct NestedSB global_nested_sb_constant_neg_count = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = -1, + // expected-error@+1{{negative size value of -1 for 'global_nested_sb_constant_neg_count.inner.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0x0 + }, + .count = -1, + // expected-error@+1{{negative size value of -1 for 'global_nested_sb_constant_neg_count.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0 +}; + + +void constant_pos_count_nullptr(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + 0x0, + 0x0 + }; +} + + +struct sb_with_other_data global_sb_with_other_data_constant_pos_count_nullptr = + (struct sb_with_other_data) { + // expected-error@+1{{initializing 'global_sb_with_other_data_constant_pos_count_nullptr.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .count = 100, + .other = 0x0 +}; + +struct NestedSB global_nested_sb_constant_pos_count_nullptr = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = 100, + // expected-error@+1{{initializing 'global_nested_sb_constant_pos_count_nullptr.inner.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .other = 0x0 + }, + .count = 100, + // expected-error@+1{{initializing 'global_nested_sb_constant_pos_count_nullptr.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .other = 0 +}; + + +void bad_arr_size(struct sb_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with array 'arr' (which has 3 bytes) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct sb_with_other_data global_sb_with_other_data_bad_arr_size = + (struct sb_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct sb_with_other_data* s, char* ptr) { // expected-note{{consider adding '__sized_by(100)' to 'ptr'}} + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with 'char *__single' and pointee of size 1 always fails}} + ptr, + 0x0 + }; +} + + +void bad_implicit_zero_count(struct sb_with_other_data* s, char*__bidi_indexable ptr, struct NestedSB* nsb) { + *s = (struct sb_with_other_data){ + // expected-warning@+1{{'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + }; + + *nsb = (struct NestedSB) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct sb_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + } + }; +} + + +void unknown_count_and_single_ptr_src(struct sb_with_other_data* s, char* ptr, struct NestedSB* nsb) { + int count = get_count(); + *s = (struct sb_with_other_data){ + count, // expected-note{{size initialized here}} + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + ptr, + 0x0 + }; + + *nsb = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }, + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct sb_with_other_data[]' with non-constant array of type 'struct sb_with_other_data[2]'}} + struct sb_with_other_data arr[] = (struct sb_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer get_count() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_sb(int new_count) { + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c b/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c new file mode 100644 index 0000000000000..a33b3abd0ff6c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c @@ -0,0 +1,424 @@ +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +void consume_sbon_with_other_data(struct sbon_with_other_data); + +struct NestedSBON { + struct sbon_with_other_data inner; + int count; + char* __sized_by_or_null(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct sbon_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct sbon_with_other_data global_no_diags = (struct sbon_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + +struct sbon_with_other_data field_initializers_with_side_effects(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct sbon_with_other_data s2 = (struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_sbon_with_other_data((struct sbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct sbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_sbon_with_other_data { + struct sbon_with_other_data s; + int other; + }; + (void)(struct Contains_sbon_with_other_data) { + .s = { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedSBON) { + // expected-warning@+1{{initializer (struct sbon_with_other_data){.count = 0, .buf = 0, .other = side_effect()} has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct sbon_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct sbon_with_other_data[]){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_sbon_with_other_data { + struct sbon_with_other_data u; + int other; + }; + + (void)(union UnionWith_sbon_with_other_data) { + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct sbon_with_other_data){ + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct sbon_with_other_data) { 0, 0x0, side_effect()}; +} + +struct sbon_with_other_data global_sbon_with_other_data_side_effect_init = + (struct sbon_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct sbon_with_other_data side_effects_in_ptr(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct sbon_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct sbon_with_other_data side_effects_in_count(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct sbon_with_other_data ){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + -1, + // ok + 0x0, + 0x0 + }; +} + +struct sbon_with_other_data global_sbon_with_other_data_constant_neg_count = + (struct sbon_with_other_data) { + .buf = 0x0, // ok + .count = -1, + .other = 0x0 +}; + +struct NestedSBON global_nested_sbon_constant_neg_count = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = -1, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = -1, + .buf = 0x0, // OK + .other = 0 +}; + + + +void constant_pos_count_nullptr(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 100, + // ok + 0x0, + 0x0 + }; +} + +struct sbon_with_other_data global_sbon_with_other_data_constant_pos_count_nullptr = + (struct sbon_with_other_data) { + .buf = 0x0, // ok + .count = 100, + .other = 0x0 +}; + +struct NestedSBON global_nested_sbon_constant_pos_count_nullptr = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = 100, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = 100, + .buf = 0x0, // OK + .other = 0 +}; + + +void bad_arr_size(struct sbon_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct sbon_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and size value of 100 with array 'arr' (which has 3 bytes) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct sbon_with_other_data global_sbon_with_other_data_bad_arr_size = + (struct sbon_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + + +void bad_count_and_single_ptr_src(struct sbon_with_other_data* s, char* ptr) { + *s = (struct sbon_with_other_data){ + 100, + ptr, + 0x0 + }; +} + + +void bad_implicit_zero_count(struct sbon_with_other_data* s, char*__bidi_indexable ptr, struct NestedSBON* nsbon) { + *s = (struct sbon_with_other_data){ + // expected-warning@+1{{'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + }; + + *nsbon = (struct NestedSBON) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct sbon_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + } + }; +} + + +void unknown_count_and_single_ptr_src(struct sbon_with_other_data* s, char* ptr, struct NestedSBON* nsbon) { + int count = get_count(); + *s = (struct sbon_with_other_data){ + count, // expected-note{{size initialized here}} + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + ptr, + 0x0 + }; + + *nsbon = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1 unless the pointer is null}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }, + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1 unless the pointer is null}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct sbon_with_other_data[]' with non-constant array of type 'struct sbon_with_other_data[2]'}} + struct sbon_with_other_data arr[] = (struct sbon_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer get_count() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_sbon(int new_count) { + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c b/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c new file mode 100644 index 0000000000000..c78b668527951 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByData { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +void TestCountedBy(struct CountedByData *d) { + int arr[10]; + d->bp = &arr[1]; // expected-note{{previously assigned here}} + d->bp2 = arr; // expected-note{{previously assigned here}} + d->l = 9; // expected-note{{previously assigned here}} + + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'bp' must be simplified; keep only one of the assignments}} + d->bp = d->bp; + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'bp2' must be simplified; keep only one of the assignments}} + d->bp2 = &arr[0]; + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'l' must be simplified; keep only one of the assignments}} + d->l = d->l; +} + +void AnyCall(void); + +void TestCountedByCallInBetween(struct CountedByData *d) { + int arr[10]; + d->bp = &arr[1]; + d->bp2 = arr; + d->l = 9; + + AnyCall(); + + d->bp = d->bp; + d->bp2 = &arr[0]; + d->l = d->l; +} + +void TestCountedByMultipleInstances(struct CountedByData *d1, struct CountedByData *d2) { + int arr[10]; + d1->bp = &arr[1]; + d1->bp2 = arr; + d1->l = 9; + + d2->bp = d1->bp; + d2->bp2 = &arr[0]; + d2->l = d1->l; +} + +struct EndedByData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void TestEndedBy(struct EndedByData *d) { + int arr[10]; + d->start = arr + 1; // expected-note{{previously assigned here}} + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'start' must be simplified; keep only one of the assignments}} + d->start = arr; + d->iter = arr; // expected-note{{previously assigned here}} + d->end = arr + 10; // expected-note{{previously assigned here}} + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'end' must be simplified; keep only one of the assignments}} + d->end = &arr[0] + 5; + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'iter' must be simplified; keep only one of the assignments}} + d->iter = &arr[0] + 6; +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c b/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c new file mode 100644 index 0000000000000..126444e61d585 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int len; + unsigned fam[__counted_by(len)]; +}; + +struct S *g_s = (struct S *)(unsigned char[1]){}; // expected-error{{initializer element is not a compile-time constant}} + +int f_s(struct S *); + +void foo(void) { + int result = f_s((struct S *)(unsigned char[1]){}); // ok +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c b/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c new file mode 100644 index 0000000000000..370d4b7084e72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s + +#include +#include + +int32_t *__single p = (int32_t *__single)(int64_t *__single)0; + +// expected-error@+1{{initializer element is not a compile-time constant}} +int32_t *__single q = (int32_t *__single)(int16_t *__single)0; diff --git a/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c b/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c new file mode 100644 index 0000000000000..5191aac64ba0b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s + +#include + +void foo(long len, int *buf __counted_by(len)) { +// This is a reduced test that exercises BoundsSafetyPointerCast +// in ExprConstant's pointer constant evaluation when source +// pointer type is counted, invoked from pointer arithmetic +// overflow check. + buf + 0; +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c b/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c new file mode 100644 index 0000000000000..9f402a13d82d0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct { + unsigned int *__counted_by(length) data; + int length; +} Item; + +// expected-note@+1{{'_oidRsa' declared here}} +static unsigned int _oidRsa[] = { 0, 1, 2 }; +// expected-error@+1{{initializing 'oidRsa.data' of type 'unsigned int *__single __counted_by(length)' (aka 'unsigned int *__single') and count value of 4 with array '_oidRsa' (which has 3 elements) always fails}} +const Item oidRsa = { _oidRsa, sizeof(_oidRsa)/sizeof(int) + 1 }; + +int main() { + return oidRsa.data[3]; +} diff --git a/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c b/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c new file mode 100644 index 0000000000000..9403b80b68b0c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int arr[4]; +int len = 4; +int *__bidi_indexable ptrBidi = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 16))[10]; +int *__bidi_indexable ptrBidi2 = __unsafe_forge_bidi_indexable(int *, arr + 2, 2); +int *__bidi_indexable ptrBidi3 = __unsafe_forge_bidi_indexable(int *, 8000, len); // expected-error{{initializer element is not a compile-time constant}} +int *__indexable ptrArr = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[-1]; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable ptrArr2 = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[1]; +int *__indexable ptrArr3 = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[2]; // expected-error{{initializer element is not a compile-time constant}} +// XXX: can't work until __unsafe_forge_single has a size +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" +int *__indexable ptrArr4 = __unsafe_forge_single(int *, 10); // expected-error{{initializer element is not a compile-time constant}} +#pragma clang diagnostic pop +int *__single ptrSingle = __unsafe_forge_bidi_indexable(int *, arr, 1); // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle2 = __unsafe_forge_bidi_indexable(int *, 0, 0); +int *__single ptrSingle12 = __unsafe_forge_bidi_indexable(int *, 0, 4); +int *__single ptrSingle3 = __unsafe_forge_bidi_indexable(int *, 2, 4) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle4 = __unsafe_forge_bidi_indexable(int *, 2, 5) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle5 = __unsafe_forge_bidi_indexable(int *, 2, 7) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle11 = __unsafe_forge_bidi_indexable(int *, 2, 8) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle6 = __unsafe_forge_single(int *, 0); +int *__single ptrSingle7 = &((int*)__unsafe_forge_bidi_indexable(int *, arr, 16))[1]; +int *__single ptrSingle8 = &((int*)__unsafe_forge_bidi_indexable(int *, arr, 16))[-2]; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle9 = __unsafe_forge_bidi_indexable(int *, &arr[4], 8) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle10 = __unsafe_forge_bidi_indexable(int *, &arr[4], 8) + 8; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle13 = __unsafe_forge_single(int *, arr); +int *__terminated_by(5) ptrTerminated = __unsafe_forge_terminated_by(int *, arr, 5); +int *__terminated_by(5) ptrTerminated2 = __unsafe_forge_terminated_by(int *, arr, 6); // expected-error{{pointers with incompatible terminators initializing 'int *__single __terminated_by(5)' (aka 'int *__single') with an expression of incompatible type 'int *__single __terminated_by(6)' (aka 'int *__single')}} +int *__terminated_by(5) ptrTerminated3 = __unsafe_forge_terminated_by(int *, 7, 5); +// The attribute on cast is missing macro identifer info. +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(4)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} +int *__terminated_by(4) ptrTerminated4 = __unsafe_forge_terminated_by(int *, arr, len); +int *__null_terminated ptrNullTerminated = __unsafe_forge_null_terminated(int *, arr); +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +int *__terminated_by(0) ptrTerminated5 = __unsafe_forge_terminated_by(int *, 0, arr); +int *__terminated_by(0) ptrTerminated6 = __unsafe_forge_terminated_by(int *, 0, 0); +// expected-error@+3{{initializer element is not a compile-time constant}} +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +int *__terminated_by(0) ptrTerminated7 = __unsafe_forge_terminated_by(int *, 1, arr); + +// rdar://84175702 +char *c = __unsafe_forge_bidi_indexable(char *, "a", 3); // expected-error{{initializer element is not a compile-time constant}} + +// expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +// expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} +// expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} +char *__null_terminated ptrNt1 = __unsafe_forge_bidi_indexable(char *, arr, 10); + +// expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +// expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} +// expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} +char *__null_terminated ptrNt2 = __unsafe_forge_bidi_indexable(char *, arr+12, 10); + +// expected-error@+2{{initializer element is not a compile-time constant}} +// expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +char *__null_terminated ptrNt3 = __unsafe_forge_single(char *, arr+12); + +// expected-error@+1{{initializer element is not a compile-time constant}} +char *__null_terminated ptrNt4 = __unsafe_null_terminated_from_indexable(__unsafe_forge_bidi_indexable(char *, arr+12, 10)); + +char *__null_terminated ptrNt5 = __unsafe_forge_null_terminated(char *, __unsafe_forge_bidi_indexable(char *, arr+12, 10)); + +char *__null_terminated ptrNt6 = __unsafe_forge_null_terminated(char *, __unsafe_forge_single(char *, arr+2)); diff --git a/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c new file mode 100644 index 0000000000000..8eb103a3134b4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int d; + struct { + int *__counted_by(l) bp; + int *bp2; + int l; + } nested; +}; + +int main() { + struct S s; + + int arr[10]; + s.nested.bp = arr; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.nested.bp' requires corresponding assignment to 's.nested.l'; add self assignment 's.nested.l = s.nested.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-assigns.c b/clang/test/BoundsSafety/Sema/count-attr-assigns.c new file mode 100644 index 0000000000000..08d69462bdcc1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-assigns.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2; + int l; +}; + +int main() { + struct S s; + + int arr[10]; + s.bp = arr; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.bp' requires corresponding assignment to 's.l'; add self assignment 's.l = s.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c b/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c new file mode 100644 index 0000000000000..6bd19bd34d447 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{'ints' declared here}} +int ints[16]; +// expected-note@+1{{'chars' declared here}} +char chars[16]; + +struct CountInt { + int *__counted_by(len) p; + unsigned len; +}; + +// ok +struct CountInt ci1 = {.p = ints, .len = sizeof(ints) / sizeof(int)}; + +// ok +struct CountInt ci2 = {.p = ints, .len = sizeof(ints) / sizeof(int) - 1}; + +// expected-error@+1{{initializing 'ci3.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 17 with array 'ints' (which has 16 elements) always fails}} +struct CountInt ci3 = {.p = ints, .len = sizeof(ints) / sizeof(int) + 1}; + +// expected-error@+2{{initializing 'ci4.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and size value of 64 with array 'chars' (which has 16 bytes) always fails}} +// expected-warning@+1{{incompatible pointer types initializing 'int *__single __counted_by(len)' (aka 'int *__single') with an expression of type 'char[16]'}} +struct CountInt ci4 = {.p = chars, .len = sizeof(chars)}; + +// expected-warning@+1{{incompatible pointer types initializing 'int *__single __counted_by(len)' (aka 'int *__single') with an expression of type 'char[16]'}} +struct CountInt ci5 = {.p = chars, .len = sizeof(chars) / sizeof(int)}; + +struct CountChar { + char *__counted_by(len) p; + unsigned len; +}; + +// ok +struct CountChar cc1 = {.p = chars, .len = sizeof(chars)}; + +// expected-warning@+1{{incompatible pointer types initializing 'char *__single __counted_by(len)' (aka 'char *__single') with an expression of type 'int[16]'}} +struct CountChar cc2 = {.p = ints, .len = sizeof(ints) / sizeof(int)}; + +// expected-warning@+1{{incompatible pointer types initializing 'char *__single __counted_by(len)' (aka 'char *__single') with an expression of type 'int[16]'}} +struct CountChar cc3 = {.p = ints, .len = sizeof(ints)}; + +struct NestedCount { + char buf[16]; + + struct { + char *__counted_by(len) p; + unsigned len; + } nested; +}; + +// ok +struct NestedCount ni1 = {.nested = {.p = ni1.buf, .len = sizeof(ni1.buf)}}; diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c new file mode 100644 index 0000000000000..6a543696cdf8c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int foo(int *__counted_by(*out_len)* out_ptr, int *out_len) { + int arr[10]; + *&*(&*out_ptr) = arr; + *out_len = 10; + if (*out_len == 10) { + *&*out_ptr = &arr[0] + 1; // expected-error{{assignment to 'int *__single __counted_by(*out_len)' (aka 'int *__single') '*out_ptr' requires corresponding assignment to '*out_len'; add self assignment '*out_len = *out_len' if the value has not changed}} + } + return 0; +} + diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c new file mode 100644 index 0000000000000..f67a6cf8eca5b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct S { + int haha; + struct { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; + } nested; +}; + +int foo(); + +int main () { + int arr[16] = {0}; + // expected-error@+1{{implicitly initializing 's.nested.bp2' of type 'int *__single __counted_by(l + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct S s; + s.nested.bp = &arr[3]; + s.nested.bp2 = &arr[3]; + s.nested.l = 9; + // run-time check here + + for (int i = 0; i < s.nested.l; ++i) + s.nested.bp[i] = i; + + s.nested.l = 10; // expected-error{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.nested.bp2'; add self assignment 's.nested.bp2 = s.nested.bp2' if the value has not changed}} + s.nested.bp = s.nested.bp; + + return s.nested.bp[9]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c new file mode 100644 index 0000000000000..5d26367732106 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int Test(struct S s) { + int arr[16] = {0}; + s.bp = &arr[3]; + s.bp2 = &arr[3]; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.l = 10; // expected-error{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + s.bp = s.bp; + + return s.bp[9]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c new file mode 100644 index 0000000000000..1ef9f9508876d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + struct { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; + } nested; +}; + +int foo(); + +int main () { + int arr[10]; + // expected-error@+1{{implicitly initializing 's.nested.bp2' of type 'int *__single __counted_by(l + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct S s; + s.nested.bp = &arr[1]; + s.nested.bp2 = arr; + s.nested.l = 9; + // run-time check here + + for (int i = 0; i < s.nested.l; ++i) + s.nested.bp[i] = i; + + // expected-error@+2{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.nested.bp2'; add self assignment 's.nested.bp2 = s.nested.bp2' if the value has not changed}} + // expected-error@+1{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.nested.bp'; add self assignment 's.nested.bp = s.nested.bp' if the value has not changed}} + s.nested.l = 11; + + return s.nested.bp2[11] + s.nested.bp[10]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c new file mode 100644 index 0000000000000..fa5fb87f89eb4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int bar (struct S s) { + int arr[10]; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + // expected-error@+2{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + // expected-error@+1{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.bp'; add self assignment 's.bp = s.bp' if the value has not changed}} + s.l = 11; + + return s.bp2[11] + s.bp[10]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c new file mode 100644 index 0000000000000..b08d0fa1d4832 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo (struct S s) { + int arr[10]; + struct S *sp = &s; + ((struct S*)sp)->bp = &arr[1]; + ((struct S*)sp)->bp2 = arr; + ((struct S*)sp)->l = 9; + + if (s.l == 8) { + // 'bp2' is also associated with 'l' but the compiler complains once at the first assignment of the group + ((struct S*)sp)->bp = &arr[1]; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->bp' requires corresponding assignment to 'sp->l'; add self assignment 'sp->l = sp->l' if the value has not changed}} + ((struct S*)sp)->bp2 = arr; + } + + return s.bp2[8] + s.bp[7]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attrs.c b/clang/test/BoundsSafety/Sema/count-attrs.c new file mode 100644 index 0000000000000..6c01b3fd9d9a3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attrs.c @@ -0,0 +1,52 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x c %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x c++ %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x objective-c %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x objective-c++ %s + +#include + +void foo(int *__counted_by(len + 1) buf, int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +void bar(int *__counted_by(len) *buf __counted_by(len + 2), int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +void baz(int *__counted_by(len) *__counted_by(len+2) buf, int len); + +void byte_foo(int *__sized_by(len + 1) buf, int len); +// expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_bar(int *__sized_by(len) *buf __sized_by(len + 2), int len); +// expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_baz(int *__sized_by(len) *__sized_by(len+2) buf, int len); +void count_vla(int len, int buf[len + 1]); + +void frob(int *__sized_by(len) buf, int len) { + // expected-error-re@+1{{__typeof__ on an expression of type 'int *{{.*}}__sized_by(len)' (aka 'int *{{.*}}') is not yet supported}} + __typeof__(buf) buf2; +} + +void nicate(int *__sized_by(0) buf) { + __typeof__(buf) buf2; // OK +} + +void foo_or_null(int *__counted_by_or_null(len + 1) buf, int len); +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void bar_or_null(int *__counted_by_or_null(len) *buf __counted_by_or_null(len + 2), int len); +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void baz_or_null(int *__counted_by_or_null(len) *__counted_by_or_null(len+2) buf, int len); + +void byte_foo_or_null(int *__sized_by_or_null(len + 1) buf, int len); +// expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_bar_or_null(int *__sized_by_or_null(len) *buf __sized_by_or_null(len + 2), int len); +// expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_baz_or_null(int *__sized_by_or_null(len) *__sized_by_or_null(len+2) buf, int len); +void count_vla_or_null(int len, int buf[len + 1]); + +void frob_or_null(int *__sized_by_or_null(len) buf, int len) { + // expected-error-re@+1{{__typeof__ on an expression of type 'int *{{.*}}__sized_by_or_null(len)' (aka 'int *{{.*}}') is not yet supported}} + __typeof__(buf) buf2; +} + +void nicate_or_null(int *__sized_by_or_null(0) buf) { + __typeof__(buf) buf2; // OK +} diff --git a/clang/test/BoundsSafety/Sema/count-bound-attrs.c b/clang/test/BoundsSafety/Sema/count-bound-attrs.c new file mode 100644 index 0000000000000..0ab0e3e09fffc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-bound-attrs.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Test(void) { + int len; + int *__bidi_indexable __counted_by(len) boundCount; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int len2; + int *__single __counted_by(len2) thinCount; + int len3; + int *__indexable __counted_by(len3) arrayCount; // expected-error{{pointer cannot be '__counted_by' and '__indexable' at the same time}} + int len5; + int *__unsafe_indexable __counted_by(len5) unsafeCount; + int len6; + int *__counted_by(len6) justCount; + int len7; + int *__counted_by(len7) __counted_by(len7+1) thinCountCount; // expected-error{{pointer cannot have more than one count attribute}} + + int len8; + int *__counted_by(len8) __single countThin; + int len9; + int *__counted_by(len9) __bidi_indexable countBound; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int len10; + int *__counted_by(len10) __indexable countArray; // expected-error{{pointer cannot be '__counted_by' and '__indexable' at the same time}} + int len12; + int *__counted_by(len12) __unsafe_indexable countUnsafe; + + int len13; + void *__counted_by(len13) countVoid; // expected-error{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} + int len14; + void *__sized_by(len14) byteCountVoid; +} diff --git a/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c b/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c new file mode 100644 index 0000000000000..78a62cbc3bf91 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c @@ -0,0 +1,340 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Mix { + int buf_count; + int *__counted_by(buf_count) buf; + int flex_count; + int fam[__counted_by(flex_count)]; +}; + +void assign_to_buf_count1(struct Mix *p) { + p->buf_count = 0; + p->buf = 0; +} + + +void assign_to_buf_count2(struct Mix *p) { + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_flex_count1(struct Mix *p) { + p->flex_count = 0; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_flex_count2(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; +} + +void assign_to_mix1(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; + p->buf_count = 0; + p->buf = 0; +} + +void assign_to_mix2(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_mix3(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->buf = 0; // expected-error{{assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf' requires corresponding assignment to 'p->buf_count'; add self assignment 'p->buf_count = p->buf_count' if the value has not changed}} + // Below is error because the assign to p is interrupted by p->buf = 0; + p->flex_count = in_count; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_mix4(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} + // Below is error because the assign to p is interrupted by p->count = 0; + p->flex_count = in_count; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + + +struct MixInner { + int buf_count; + int *__counted_by(buf_count) buf; + int flex_count; +}; + +struct MixOuter { + struct MixInner header; + int fam[__counted_by(header.flex_count)]; +}; + + +struct MixOuterSharedCount { + struct MixInner header; + int fam[__counted_by(header.buf_count)]; +}; + +void assign_to_inner_buf_count1(struct MixInner *p) { + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_inner_buf_count2(struct MixInner *p) { + p->buf_count = 0; + p->buf = 0; +} + +void assign_to_header_buf_count1(struct MixOuter *p) { + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->header.buf'; add self assignment 'p->header.buf = p->header.buf' if the value has not changed}} +} + +void assign_to_header_buf_count2(struct MixOuter *p) { + p->header.buf_count = 0; + p->header.buf = 0; +} + +void assign_to_header_flex_count(struct MixOuter *p) { + p->header.flex_count = 0; // expected-error{{assignment to 'p->header.flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_header_shared_count1(struct MixOuterSharedCount *p) { + // expected-error@+1{{assignment to 'p->header.buf_count' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->header.buf'; add self assignment 'p->header.buf = p->header.buf' if the value has not changed}} +} + +void assign_to_header_shared_count2(struct MixOuterSharedCount *p) { + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->header.buf = 0; +} + +struct Indirect { + int len1; + int len2; + int * __counted_by(len1) p1; + int * __counted_by(len1 + len2) p2; + int fam[__counted_by(len2)]; +}; + +void assign_indirect_group(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p1 = p1; + ptr->p2 = p2; +} + +void assign_indirect_group_scramble(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->p1 = p1; + ptr->p2 = p2; + ptr->len1 = len1; + ptr->len2 = len2; +} + +void assign_indirect_group_scramble2(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len1 = len1; + ptr->p1 = p1; + ptr->len2 = len2; + ptr->p2 = p2; +} + +void assign_indirect_group_len1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p1 = p1; + ptr->p2 = p2; +} + +void assign_indirect_group_len1_p1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p2 = p2; +} + +void assign_indirect_group_p1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1'; add self assignment 'ptr->p1 = ptr->p1' if the value has not changed}} + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p2 = p2; +} + +void assign_indirect_p2_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2'; add self assignment 'ptr->p2 = ptr->p2' if the value has not changed}} + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p1 = p1; +} + +void assign_indirect_p2_len2_missing(struct Indirect * __bidi_indexable p, int len1, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2'; add self assignment 'ptr->p2 = ptr->p2' if the value has not changed}} + ptr->len1 = len1; + ptr->p1 = p1; +} + +void assign_indirect_p2_len2_len1_missing(struct Indirect * __bidi_indexable p, int len1, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p1 = p1; +} + +void update_indirect_group(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; + p->p2 = p2; +} + +void update_indirect_group_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->p1 = p1; + p->p2 = p2; + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; +} + +void update_indirect_group_scramble2(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + p->p1 = p1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p2 = p2; +} + +void update_indirect_group_len1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-error@+1{{assignment to 'p->len2' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; + p->p2 = p2; +} + +void update_indirect_group_len1_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + // expected-error@+1{{assignment to 'p->len2' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p2 = p2; +} + +void update_indirect_group_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1'; add self assignment 'p->p1 = p->p1' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p2 = p2; +} + +void update_indirect_p2_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; +} + +void update_indirect_p2_len2_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->p1 = p1; +} + +void update_indirect_p2_len2_len1_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; +} + + +void self_assign_update_indirect_group(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + p = p; + p->len1 = len1; + p->len2 = len2; + p->p1 = p1; + p->p2 = p2; +} + +void self_assign_update_indirect_group_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + p = p; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + p->p2 = p2; +} + +void self_assign_update_indirect_group_scramble2(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->p1 = p1; + p->p2 = p2; + p = p; + p->len1 = len1; + // expected-error@+2{{assignments to dependent variables should not have side effects between them}} + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; +} + +void self_assign_update_indirect_group_len1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + p = p; + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; + p->p2 = p2; +} + +void self_assign_update_indirect_group_len1_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + p = p; + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p2 = p2; +} + +void self_assign_update_indirect_group_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1'; add self assignment 'p->p1 = p->p1' if the value has not changed}} + p->len1 = len1; + p->len2 = len2; + p->p2 = p2; +} + +void self_assign_update_indirect_p2_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->len2 = len2; + p->p1 = p1; +} + +void self_assign_update_indirect_p2_missing_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p = p; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + p->p1 = p1; +} + +void self_assign_update_indirect_p2_len2_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->p1 = p1; +} + +void self_assign_update_indirect_p2_len2_len1_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c b/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c new file mode 100644 index 0000000000000..08ee21c0a6944 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +struct parent { + struct { + union { + struct { + int *__counted_by(len) entries; + int len; + }; + int *other; + }; + } s; +}; diff --git a/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c b/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c new file mode 100644 index 0000000000000..201f60eb8fbd9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify=with-checks -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update %s +// RUN: %clang_cc1 -fsyntax-only -verify=without-checks -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s +#include + +// without-checks-no-diagnostics + +void foo(int *__counted_by(count) x, int count) { + // with-checks-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'x' requires corresponding assignment to 'count'; add self assignment 'count = count' if the value has not changed}} + *x++ = 0; +} + +void bar(int *__counted_by(count) x, int count) { + // with-checks-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'x' requires corresponding assignment to 'count'; add self assignment 'count = count' if the value has not changed}} + *(x = x+1) = 0; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c b/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c new file mode 100644 index 0000000000000..84a0613c49a26 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c @@ -0,0 +1,72 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void funcAttr1(int * __counted_by_or_null(len) p, int len) __attribute__((nonnull(1))); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void funcAttr2(int * __counted_by_or_null(len) p, int len) __attribute__((nonnull)); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +__attribute__((nonnull)) void funcAttr3(int * __counted_by_or_null(len) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +int * __counted_by_or_null(len) funcAttr4(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __counted_by_or_null(len) funcAttr5(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __counted_by_or_null(len) funcAttr6(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +void funcAttr7(int * __sized_by_or_null(len) p, int len) __attribute__((nonnull(1))); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void funcAttr8(int * __sized_by_or_null(len) p, int len) __attribute__((nonnull)); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +__attribute__((nonnull)) void funcAttr9(int * __sized_by_or_null(len) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +int * __sized_by_or_null(len) funcAttr10(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __sized_by_or_null(len) funcAttr11(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __sized_by_or_null(len) funcAttr12(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +void funcAttr13(int * __counted_by(len) p, int len) __attribute__((nonnull(1))); +void funcAttr14(int * __counted_by(len) p, int len) __attribute__((nonnull)); +__attribute__((nonnull)) void funcAttr15(int * __counted_by(len) p, int len); +int * __counted_by(len) funcAttr16(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __counted_by(len) funcAttr17(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __counted_by(len) funcAttr18(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +int * __counted_by_or_null(len) returnAttr1(int len) __attribute__((returns_nonnull)); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} +__attribute__((returns_nonnull)) int * __counted_by_or_null(len) returnAttr2(int len); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} +int * __attribute__((returns_nonnull)) __counted_by_or_null(len) returnAttr3(int len); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} + +int * __sized_by_or_null(len) returnAttr4(int len) __attribute__((returns_nonnull)); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} +__attribute__((returns_nonnull)) int * __sized_by_or_null(len) returnAttr5(int len); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} +int * __attribute__((returns_nonnull)) __sized_by_or_null(len) returnAttr6(int len); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} + +int * __counted_by(len) returnAttr7(int len) __attribute__((returns_nonnull)); +__attribute__((returns_nonnull)) int * __counted_by(len) returnAttr8(int len); +int * __attribute__((returns_nonnull)) __counted_by(len) returnAttr9(int len); + +void paramAttr1(int * __attribute((nonnull)) __counted_by_or_null(len) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void paramAttr2(int * __counted_by_or_null(len) p __attribute((nonnull)), int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void paramAttr3(int * __counted_by_or_null(len) __attribute((nonnull)) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} + +void paramAttr4(int * __attribute((nonnull)) __sized_by_or_null(len) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void paramAttr5(int * __sized_by_or_null(len) p __attribute((nonnull)), int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void paramAttr6(int * __sized_by_or_null(len) __attribute((nonnull)) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} + +void paramAttr7(int * __attribute((nonnull)) __counted_by(len) p, int len); +void paramAttr8(int * __counted_by(len) p __attribute((nonnull)), int len); +void paramAttr9(int * __counted_by(len) __attribute((nonnull)) p, int len); + +void paramType1(int * _Nonnull __counted_by_or_null(len) p, int len); // expected-warning{{combining '__counted_by_or_null' and '_Nonnull'; did you mean '__counted_by' instead?}} +void paramType2(int * __counted_by_or_null(len) _Nonnull p, int len); // expected-warning{{combining '__counted_by_or_null' and '_Nonnull'; did you mean '__counted_by' instead?}} + +void paramType3(int * _Nonnull __sized_by_or_null(len) p, int len); // expected-warning{{combining '__sized_by_or_null' and '_Nonnull'; did you mean '__sized_by' instead?}} +void paramType4(int * __sized_by_or_null(len) _Nonnull p, int len); // expected-warning{{combining '__sized_by_or_null' and '_Nonnull'; did you mean '__sized_by' instead?}} + +void paramType5(int * _Nonnull __counted_by(len) p, int len); +void paramType6(int * __counted_by(len) _Nonnull p, int len); + +void paramType7(int * __counted_by(4) _Nullable p); // expected-warning{{combining '__counted_by' with non-zero count (which cannot be null) and '_Nullable'; did you mean '__counted_by_or_null' instead?}} +void paramType8(int * __counted_by_or_null(4) _Nullable p); +void paramType9(int * __sized_by(4) _Nullable p); // expected-warning{{combining '__sized_by' with non-zero size (which cannot be null) and '_Nullable'; did you mean '__sized_by_or_null' instead?}} + +void paramType10(int * _Nullable __counted_by(4) p); // expected-warning{{combining '__counted_by' with non-zero count (which cannot be null) and '_Nullable'; did you mean '__counted_by_or_null' instead?}} +void paramType11(int * _Nullable __counted_by_or_null(4) p); +void paramType12(int * _Nullable __sized_by(4) p); // expected-warning{{combining '__sized_by' with non-zero size (which cannot be null) and '_Nullable'; did you mean '__sized_by_or_null' instead?}} + diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c b/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c new file mode 100644 index 0000000000000..c3308a4b83238 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c @@ -0,0 +1,51 @@ +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s --implicit-check-not="fix-it" +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1| FileCheck %s --implicit-check-not="fix-it" + +#include + +struct my_struct { + int len; + int *__counted_by_or_null(len) tmp; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:13-[[@LINE+1]]:33}:"__counted_by" + int fam[__counted_by_or_null(len)]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +struct constant_sized_inner_arr_2d_struct { + int len; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:13-[[@LINE+1]]:33}:"__counted_by" + int fam[__counted_by_or_null(len)][10]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +// expected-error@+3{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to 'int (*)[][size]' because 'int[][size]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:52-[[@LINE+1]]:72}:"__sized_by_or_null" +void counted_nested_unsized_array(int size, int (* __counted_by_or_null(size) param)[__counted_by_or_null(10)][size]); + +void local_array() { + int local_buf[__counted_by_or_null(29)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + int local_sized_buf[__sized_by_or_null(29)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + + int local_buf_no_init[__counted_by_or_null(31)]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}} + int local_sized_buf_no_init[__sized_by_or_null(31)]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}} +} + +int global_buf[__counted_by_or_null(5)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:40-[[@LINE+1]]:60}:"__counted_by" +void counted_pointer_to_array(int (*p)[__counted_by_or_null(len)], int len); // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:38-[[@LINE+1]]:56}:"__sized_by" +void sized_pointer_to_array(int (*p)[__sized_by_or_null(size)], int size); // expected-error{{array objects cannot be null; did you mean __sized_by instead}} + +extern int len; +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:16-[[@LINE+1]]:36}:"__counted_by" +extern int arr[__counted_by_or_null(len)]; // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +extern int arr2[__counted_by(len)]; +extern int (*arr3)[__counted_by_or_null(len)]; // expected-error{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + +void local_extern() { + extern int len2; + extern int arr4[__counted_by(len2)]; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:21-[[@LINE+1]]:41}:"__counted_by" + extern int arr5[__counted_by_or_null(len2)]; // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c b/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c new file mode 100644 index 0000000000000..e2c98be990475 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct my_struct { + int len; + int *__counted_by_or_null(len) tmp; + int fam[__counted_by_or_null(len)]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +struct struct_2d { + int len; + int fam[__counted_by_or_null(len)][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +struct sized_2d_struct { + int len; + int fam[__sized_by_or_null(len)][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +int global_len; +struct variably_sized_inner_arr_2d_struct { + int len; + int fam[__counted_by_or_null(len)][global_len]; // expected-error{{fields must have a constant size: 'variable length array in structure' extension will never be supported}} +}; + +struct constant_sized_inner_arr_2d_struct { + int len; + int fam[__counted_by_or_null(len)][10]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead}} +}; + +struct constant_sized_outer_arr_2d_struct { + int len; + int fam[10][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void pointers_to_array_params(int size, int (* __sized_by_or_null(size) param)[__counted_by_or_null(10)][size], int len, int arr[__counted_by_or_null(11)][len]); + +void counted_unsized_array(int size, int (*param)[__counted_by_or_null(10)][__counted_by_or_null(size)]); // expected-error{{array has incomplete element type 'int[]'}} + +// expected-error@+2{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'int (*)[][size]' because 'int[][size]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void counted_nested_unsized_array(int size, int (* __counted_by_or_null(size) param)[__counted_by_or_null(10)][size]); + +void counted_decayed_nested(int len, int arr[__counted_by_or_null(11)][__counted_by_or_null(len)]); // expected-error{{array has incomplete element type 'int[]'}} + +void counted_decayed_of_variably_sized_array(int len, int arr[__counted_by_or_null(11)][len]); + +// expected-warning@+1{{tentative array definition assumed to have one element}} +int global_buf[__counted_by_or_null(5)]; +int global_2D_buf[__sized_by(7)][__counted_by_or_null(13)]; // expected-error{{array has incomplete element type 'int[]'}} +int global_2D_buf_const_sized_outer[7][__counted_by_or_null(13)]; // expected-error{{array has incomplete element type 'int[]'}} diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null.c b/clang/test/BoundsSafety/Sema/counted-by-or-null.c new file mode 100644 index 0000000000000..a99d5e0ebdfa6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null.c @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +void foo(int * __counted_by_or_null(2) p); + +void bar(int * __counted_by_or_null(1) p) { + foo(p); +} + +void assign_null_constant_count() { + int * __counted_by_or_null(2) p = NULL; + foo(p); +} + +void assign_null_dynamic_count(int count) { + int c = count; + int * __counted_by_or_null(c) p = NULL; + foo(p); +} + +int * __counted_by_or_null(c) quux(int c); +void side_effect(); + +void reassign_coupled_decls() { + int c = 3; + int * __counted_by_or_null(c) p; + p = quux(c); + c = c; + + side_effect(); + + p = quux(4); + c = 4; + + side_effect(); + + int * __counted_by_or_null(5) tmp = quux(5); + c = 5; + p = tmp; + + side_effect(); + + p = quux(6); // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by_or_null(c)' (aka 'int *__single') 'p' requires corresponding assignment to 'c'; add self assignment 'c = c' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + p = quux(6); // expected-error{{assignments to dependent variables should not have side effects between them}} + + side_effect(); + + c = 7; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'c' requires corresponding assignment to 'int *__single __counted_by_or_null(c)' (aka 'int *__single') 'p'; add self assignment 'p = p' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'c' must be simplified; keep only one of the assignments}} + c = 7; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c b/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c new file mode 100644 index 0000000000000..67fed66b7b3e8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(void) { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(bar) bar; + int *p = bar; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c b/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c new file mode 100644 index 0000000000000..78fc29721327e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c @@ -0,0 +1,767 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -verify=expected,legacy,extra %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -verify=expected,legacy,cli %s +#include + +void consume_cb(int* __counted_by(3) p); +void consume_cbon(int* __counted_by_or_null(3) p); + +struct cb { + int count; + int* __counted_by(count) buf; +}; + +struct cbon { + int count; + int* __counted_by_or_null(count) buf; +}; + +void side_effect(void); + +int global_arr [2] = {0}; +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(2) global_cb = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(2) global_cbon = global_arr; + +const int const_size = 2; +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(const_size) global_cb_const_qual_count = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(const_size) global_cbon_const_qual_count = global_arr; + +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(1+1) global_cb_opo = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(1+1) global_cbon_opo = global_arr; + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(2) test_cb(int* __counted_by(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(2) local_cb = p; + local_cb = p; // OK + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + --global_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb = global_cb + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(2) test_cb_constant_fold_count(int* __counted_by(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(1+1) local_cb = p; + local_cb = p; // OK + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cb_opo++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb_opo; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_opo--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + --global_cb_opo; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_opo += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_opo -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb_opo++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb_opo = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb_opo-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb_opo = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_opo = global_cb_opo + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(size) test_cb_const_qualified_size(const int size, int* __counted_by(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(local_size) local_cb = p; + side_effect(); + local_cb = p; // OK + + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_cb_const_qual_count++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb_const_qual_count; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --global_cb_const_qual_count; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_const_qual_count += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_const_qual_count = global_cb_const_qual_count + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(2) test_cbon(int* __counted_by_or_null(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(2) local_cbon = p; + local_cbon = p; // OK + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + --global_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon = global_cbon + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(2) test_cbon_constant_fold_count(int* __counted_by_or_null(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(1+1) local_cbon = p; + local_cbon = p; // OK + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cbon_opo++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon_opo; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_opo--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + --global_cbon_opo; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_opo += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_opo -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon_opo++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon_opo = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon_opo-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon_opo = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_opo = global_cbon_opo + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(size) test_cbon_const_qualified_size(const int size, int* __counted_by_or_null(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(local_size) local_cbon = p; + side_effect(); + local_cbon = p; // OK + + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_cbon_const_qual_count++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon_const_qual_count; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --global_cbon_const_qual_count; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_const_qual_count += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_const_qual_count = global_cbon_const_qual_count + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} +} + +// Warning diagnostic tests + +void downgrade_to_warning(int* __counted_by(4) ptr) { // expected-note{{__counted_by attribute is here}} +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 4 always traps}} +#pragma clang diagnostic pop +} + +void downgrade_to_ignored(int* __counted_by(4) ptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // ok +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-redecl.c b/clang/test/BoundsSafety/Sema/counted-by-redecl.c new file mode 100644 index 0000000000000..17e6f3ddb6960 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-redecl.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s + +#include + +int len_a; +// expected-note@+1{{'a' declared here}} +int * __counted_by(len_a) a; +// expected-error@+1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int * __counted_by_or_null(len_a) a; + +int len_b; +// expected-note@+1{{'b' declared here}} +int * __counted_by_or_null(len_b) b; +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int * __counted_by(len_b) b; + +int len_c; +// expected-note@+1{{'c' declared here}} +int * __sized_by(len_c) c; +// expected-error@+1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} +int * __sized_by_or_null(len_c) c; + +int len_d; +// expected-note@+1{{'d' declared here}} +int * __sized_by_or_null(len_d) d; +// expected-error@+1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int * __sized_by(len_d) d; diff --git a/clang/test/BoundsSafety/Sema/counted_by_constants.c b/clang/test/BoundsSafety/Sema/counted_by_constants.c new file mode 100644 index 0000000000000..c0e618f6f8091 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_constants.c @@ -0,0 +1,312 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// const arguments +unsigned global_count = 2; +const unsigned const_global_count = 0; +extern const unsigned extern_const_global_count; + +enum { + enum_count = 10 +}; + +struct struct_enum_count { + int *__counted_by(enum_count) buf; +}; + +struct struct_const_global_count { + int *__counted_by(const_global_count) buf; +}; + +void fun_const_global_count(int *__counted_by(const_global_count) arg); + +void test_local_const_global_count() { + int *__counted_by(const_global_count) local; +} + +struct struct_extern_const_global_count { + int *__counted_by(extern_const_global_count) buf; +}; + +void fun_extern_const_global_count(int *__counted_by(extern_const_global_count) arg); + +void test_local_extern_const_global_count() { + int *__counted_by(extern_const_global_count) local; +} + +// data const arguments +unsigned data_const_global_count __unsafe_late_const; + +struct struct_data_const_global_count { + int *__counted_by(data_const_global_count) buf; +}; + +int fun_data_const_global_count(int *__counted_by(data_const_global_count) arg); + +void test_local_data_const_global_count() { + int *__counted_by(data_const_global_count) local; +} + +// const function calls +#define __pure2 __attribute__((const)) + +__pure2 unsigned fun_const_no_argument(); + +struct struct_consteval_function_call_count { + int *__counted_by(fun_const_no_argument()) buf; +}; + +void fun_consteval_function_call_count(int *__counted_by(fun_const_no_argument()) arg); + +void test_local_consteval_function_call_count() { + int *__counted_by(fun_const_no_argument()) local; +} + +// expected-warning@+1{{'const' attribute on function returning 'void'; attribute ignored}} +__pure2 void fun_const_void(); +struct struct_void_function_call_count { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(fun_const_void()) buf; + // expected-error@+1{{'__sized_by' attribute requires an integer type argument}} + int *__sized_by(fun_const_void()) buf2; + // expected-error@+1{{'__counted_by_or_null' attribute requires an integer type argument}} + int *__counted_by_or_null(fun_const_void()) buf3; + // expected-error@+1{{'__sized_by_or_null' attribute requires an integer type argument}} + int *__sized_by_or_null(fun_const_void()) buf4; +}; + +// expected-note@+1 12{{'fun_no_argument' declared here}} +unsigned fun_no_argument(); + +struct struct_function_call_count { + // expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} + int *__counted_by(fun_no_argument()) buf; + // expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} + int *__sized_by(fun_no_argument()) buf2; + // expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} + int *__counted_by_or_null(fun_no_argument()) buf3; + // expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} + int *__sized_by_or_null(fun_no_argument()) buf4; +}; + +// expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} +void fun_function_call_count(int *__counted_by(fun_no_argument()) arg); +// expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} +void fun_function_call_size(int *__sized_by(fun_no_argument()) arg); +// expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} +void fun_function_call_count_null(int *__counted_by_or_null(fun_no_argument()) arg); +// expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} +void fun_function_call_size_null(int *__sized_by_or_null(fun_no_argument()) arg); + +void test_local_function_call_count() { + // expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} + int *__counted_by(fun_no_argument()) local; + // expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} + int *__sized_by(fun_no_argument()) local2; + // expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} + int *__counted_by_or_null(fun_no_argument()) local3; + // expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} + int *__sized_by_or_null(fun_no_argument()) local4; +} + + +// const function calls with argument +__pure2 int fun_const_with_argument(int arg); +__pure2 long fun_const_with_argument2(int arg0, const void *arg1); + + +struct struct_const_function_argument_count { + int *__counted_by(fun_const_with_argument(2)) buf; +}; + +void fun_const_function_argument_count(int *__counted_by(fun_const_with_argument(4)) arg); + +void test_local_const_function_argument_count() { + int *__counted_by(fun_const_with_argument(0)) local; +} + +struct struct_const_function_argument_count2 { + int field; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(field)) buf; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(field)) buf2; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(field)) buf3; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(field)) buf4; + const int const_field; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(const_field)) buf5; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(const_field)) buf6; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(const_field)) buf7; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(const_field)) buf8; + + int *__counted_by(fun_const_with_argument(const_global_count)) buf9; + int *__sized_by(fun_const_with_argument(const_global_count)) buf10; + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) buf11; + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) buf12; + + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(data_const_global_count)) buf13; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(data_const_global_count)) buf14; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) buf15; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) buf16; +}; + +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by' attribute is not a constant expression}} +void fun_const_function_argument_count2( + int *__counted_by(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by' attribute is not a constant expression}} +void fun_const_function_argument_size2( + int *__sized_by(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_count_null2( + int *__counted_by_or_null(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_size_null2( + int *__sized_by_or_null(fun_const_with_argument(global_count)) arg); + +void fun_const_function_argument_count3( + int *__counted_by(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_size3( + int *__sized_by(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_count_null3( + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_size_null3( + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) arg); + +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} +void fun_const_function_argument_count4( + int *__counted_by(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} +void fun_const_function_argument_size4( + int *__sized_by(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_count_null4( + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_size_null4( + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) arg); + +void test_local_const_function_argument_count2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_size2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_count_null2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_size_null2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(arg)) local; +} + +void test_local_const_function_argument_count3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_size3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_count_null3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_size_null3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(global_count)) local; +} + +void test_local_const_function_argument_count4() { + int *__counted_by(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_size4() { + int *__sized_by(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_count_null4() { + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_size_null4() { + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) local; +} + +void test_local_const_function_argument_count5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_size5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_count_null5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_size_null5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) local; +} + +struct struct_const_function_argument2_count { + int field0; + void *field1; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(field0, field1)) buf; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(field0, field1)) buf2; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(field0, field1)) buf3; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(field0, field1)) buf4; + + const int const_field0; + const void *const_field1; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_field0, field1)) buf5; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_field0, field1)) buf6; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_field0, field1)) buf7; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_field0, field1)) buf8; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_field0, const_field1)) buf9; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_field0, const_field1)) buf10; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_field0, const_field1)) buf11; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_field0, const_field1)) buf12; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_global_count, const_field1)) buf13; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_global_count, const_field1)) buf14; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_global_count, const_field1)) buf15; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_global_count, const_field1)) buf16; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(data_const_global_count, const_field1)) buf17; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(data_const_global_count, const_field1)) buf18; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(data_const_global_count, const_field1)) buf19; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(data_const_global_count, const_field1)) buf20; +}; diff --git a/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c b/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c new file mode 100644 index 0000000000000..19ea4f06d34e1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// data const arguments +unsigned data_const_global_count __unsafe_late_const; +unsigned global_count; +const unsigned const_global_count; + +enum { enum_count = 10, }; + +struct struct_enum_count { + int *__counted_by(enum_count) buf; +}; + +void test_enum_count(struct struct_enum_count *sp, int *__bidi_indexable arg) { + sp->buf = arg; +} + +struct struct_data_const_global_count { + int *__counted_by(data_const_global_count) buf; +}; + +struct struct_global_count { + // expected-error@+1{{count expression on struct field may only reference other fields of the same struct}} + int *__counted_by(global_count) buf; +}; + +struct struct_const_global_count { + int *__counted_by(const_global_count) buf; +}; + +unsigned data_const_global_count_flex __unsafe_late_const; + +struct struct_data_const_global_count_flex { + int dummy; + int flex[__counted_by(data_const_global_count_flex)]; +}; + +struct struct_global_count_flex { + int dummy; + // expected-error@+1{{count expression on struct field may only reference other fields of the same struct}} + int flex[__counted_by(global_count)]; +}; +struct struct_const_global_count_flex { + int dummy; + int flex[__counted_by(const_global_count)]; +}; + +void fun_update_data_const_global_count_flex(unsigned count) { + data_const_global_count_flex = 100; +} + +void fun_const_global_count(int *__counted_by(data_const_global_count) arg) { + int arr[10]; + arg = arr; +} + +// expected-error@+1{{count expression in function declaration may only reference parameters of that function}} +void fun_global_count(int *__counted_by(global_count) arg); + +// expected-error@+1{{count expression in function declaration may only reference parameters of that function}} +void fun_global_array_count(int arg[__counted_by(global_count)]); + +void fun_data_const_init() { + data_const_global_count = 10; +} + +void test_local_data_const_global_count(int *__bidi_indexable arg) { + int *__counted_by(data_const_global_count) local; + local = arg; +} + +void test_local_global_count(int *__bidi_indexable arg) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(global_count) local; +} + +void test_data_const_struct_init() { + struct struct_data_const_global_count s; // better be an error +} + +void test_data_const_struct_init2(int *__indexable arg) { + struct struct_data_const_global_count s = { arg }; +} + +void test_data_const_struct_init3(int *__indexable arg) { + struct struct_data_const_global_count s; // better be an error + s.buf = arg; +} + +void test_data_const_struct_init4(void *__bidi_indexable arg) { + struct struct_data_const_global_count *sp = + (struct struct_data_const_global_count *)arg; +} + +void test_global_const_struct_init(struct struct_const_global_count *sp, + int *__bidi_indexable arg) { + sp->buf = arg; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/counted_by_global_assign.c b/clang/test/BoundsSafety/Sema/counted_by_global_assign.c new file mode 100644 index 0000000000000..efd8f9d7af3c8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_global_assign.c @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int len; +int *__counted_by(len) ptr; + +void test_len_assign() { + len = 0; + // expected-error@-1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'ptr'; add self assignment 'ptr = ptr' if the value has not changed}} +} + +void test_ptr_assign(int *__bidi_indexable arg) { + ptr = arg; + // expected-error@-1{{assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'ptr' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} +} + +void test_assign(int *__bidi_indexable arg) { + len = 1; + ptr = arg; +} + +extern int extlen; +extern int *__counted_by(extlen) extptr; + +void test_extlen_assign() { + extlen = 0; + // expected-error@-1{{assignment to 'extlen' requires corresponding assignment to 'int *__single __counted_by(extlen)' (aka 'int *__single') 'extptr'; add self assignment 'extptr = extptr' if the value has not changed}} +} + +void test_extptr_assign(int *__bidi_indexable arg) { + extptr = arg; + // expected-error@-1{{assignment to 'int *__single __counted_by(extlen)' (aka 'int *__single') 'extptr' requires corresponding assignment to 'extlen'; add self assignment 'extlen = extlen' if the value has not changed}} +} + +void test_ext_assign(int *__bidi_indexable arg) { + extlen = 1; + extptr = arg; +} + +int shared_len = 0; +int *__counted_by(shared_len) ptr2; +char *__counted_by(shared_len) ptr3; + +void test_shared_len_assign() { + // expected-error@+2{{assignment to 'shared_len' requires corresponding assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2'; add self assignment 'ptr2 = ptr2' if the value has not changed}} + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3'; add self assignment 'ptr3 = ptr3' if the value has not changed}} + shared_len = 0; +} + +void test_ptr2_assign(int *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2' requires corresponding assignment to 'shared_len'; add self assignment 'shared_len = shared_len' if the value has not changed}} + ptr2 = arg; +} + +void test_ptr3_assign(char *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3' requires corresponding assignment to 'shared_len'; add self assignment 'shared_len = shared_len' if the value has not changed}} + ptr3 = arg; +} + +void test_shared_len_ptr2_assign(int *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3'; add self assignment 'ptr3 = ptr3' if the value has not changed}} + shared_len = 1; + ptr2 = arg; +} + +void test_shared_len_ptr3_assign(char *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2'; add self assignment 'ptr2 = ptr2' if the value has not changed}} + shared_len = 1; + ptr3 = arg; +} + + +void test_shared_assign(int *__bidi_indexable arg1, + char *__bidi_indexable arg2) { + shared_len = 1; + ptr3 = arg2; + ptr2 = arg1; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_incdec.c b/clang/test/BoundsSafety/Sema/counted_by_incdec.c new file mode 100644 index 0000000000000..ca63e8a006b99 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_incdec.c @@ -0,0 +1,159 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__counted_by(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void FooOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by_or_null(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void Bar(int *__counted_by(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void BarOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void TestPtrPostIncrement(int *__counted_by(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestPtrPostIncrementOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestLenPostIncrementOrNull(int *__counted_by_or_null(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} rdar://116470062 +} + +void TestLenPostIncrement(int *__counted_by(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestPtrPreDecrement(int *__counted_by(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} +} + +void TestPtrPreDecrementOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} +} + +typedef struct { + char *__counted_by(len1 + len2) buf; + unsigned len1; + unsigned len2; +} S; + +void TestMultipleCounts1(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__counted_by_or_null(len1 + len2) buf; + unsigned len1; + unsigned len2; +} SOrNull; + +void TestMultipleCounts1OrNull(SOrNull *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2OrNull(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3OrNull(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__counted_by(len) buf; + unsigned len; +} T; + +void Baz(T *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void Qux(T *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void Quux(T *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void Quuz(T *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void Corge(T *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +typedef struct { + char *__counted_by_or_null(len) buf; + unsigned len; +} TOrNull; + +void BazOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void QuxOrNull(TOrNull *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void QuuxOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void QuuzOrNull(TOrNull *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void CorgeOrNull(TOrNull *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c new file mode 100644 index 0000000000000..653539b2e4c95 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct T1 { + int len; + int len2; + int *__counted_by(len) __counted_by(len+1) buf; // expected-error{{pointer cannot have more than one count attribute}} + int *__counted_by(len + len2) buf2; + int *__bidi_indexable __counted_by(len) buf3; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int *__counted_by(len) __bidi_indexable buf4; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +}; + +// expected-note@+1{{'foo' declared here}} +int foo(void); + +struct T2 { + float flen; + int *__counted_by(flen) buf1; // expected-error{{attribute requires an integer type argument}} + int *__counted_by(foo()) buf2; // expected-error{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} +}; + +int glen; + +int Test(int *__counted_by(glen) ptr); // expected-error{{count expression in function declaration may only reference parameters of that function}} + +int *__bidi_indexable __counted_by(glen) glob_buf1; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +int *__counted_by(glen) __bidi_indexable glob_buf2; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + +void foo1(int *__bidi_indexable __counted_by(glen) glob_buf1); // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +void foo2(int *__counted_by(glen) __bidi_indexable glob_buf2); // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c new file mode 100644 index 0000000000000..99a52843d5de1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete arrays + +//============================================================================== +// Extern declaration that is later redefined +//============================================================================== + + +extern int external_arr_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +extern char (* __counted_by(external_arr_len) incompleteArrayPtr)[]; // expected-note{{'incompleteArrayPtr' declared here}} +void use_incompleteArrayPtr_when_incomplete(void) { + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + char x = incompleteArrayPtr[0][0]; +} + +// Provide a complete definition with the array size defined +int external_arr_len; +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +char (* __counted_by(external_arr_len) incompleteArrayPtr)[4]; + +void use_incompleteArrayPtr_when_complete(void) { + char x = incompleteArrayPtr[0][0]; // OK? +} + +int external_arr_len2; +char (* __counted_by(external_arr_len2) CompleteArrayPtr)[4]; // OK + +//============================================================================== +// Global vars +//============================================================================== + +int global_arr_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +char (* __counted_by(global_arr_len) GlobalCBIncompleteArrayPtr)[]; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +char (* __counted_by_or_null(global_arr_len) GlobalCBONIncompleteArrayPtr)[]; + +char (* __counted_by(global_arr_len) GlobalCBCompleteArrayPtr)[2]; // OK +char (* __counted_by_or_null(global_arr_len) GlobalCBONCompleteArrayPtr)[2]; // OK + +char (* __counted_by(0) GlobalCBCompleteArrayPtrZeroCount)[2]; // OK +// expected-error@+1{{implicitly initializing 'GlobalCBCompleteArrayPtrOneCount' of type 'char (*__single __counted_by(1))[2]' (aka 'char (*__single)[2]') and count value of 1 with null always fails}} +char (* __counted_by(1) GlobalCBCompleteArrayPtrOneCount)[2]; + +//============================================================================== +// Local vars +//============================================================================== + +void local_cb(void) { + int size = 0; + // expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} + char (* __counted_by(size) local_implicit_init)[]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + local_implicit_init[0]; + + int size2 = 0; + // expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} + char (* __counted_by(size2) local_explicit_init)[] = 0x0; +} + +void local_cbon(void) { + int size = 0; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} + char (* __counted_by_or_null(size) local_implicit_init)[]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + local_implicit_init[0]; + + int size2 = 0; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} + char (* __counted_by_or_null(size2) local_explicit_init)[] = 0x0; +} + +//============================================================================== +// Parameters +//============================================================================== + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +void decl_param_cb(char (* __counted_by(size) p)[], int size) {} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void decl_param_cbon(char (* __counted_by_or_null(size) p)[], int size) {} + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +void access_param_cb(char (* __counted_by(size) p)[], int size) { + void* local = p; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0]; + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0][0]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + &p[0]; + + p = 0; // OK + size = 0; // OK +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void access_param_cbon(char (* __counted_by_or_null(size) p)[], int size) { + // This doesn't show up as an error because error recovery for treats `p` as + // having type `char (*__single __sized_by_or_null(size))[]` + void* local = p; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0]; + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0][0]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + &p[0]; + + p = 0; // OK + size = 0; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c new file mode 100644 index 0000000000000..6685f76e2a75d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete enums + +extern int external_enum_len; +typedef enum incomplete_enum incomplete_enum_t; // expected-note 2{{forward declaration of 'enum incomplete_enum'}} +extern incomplete_enum_t* __counted_by(external_enum_len) incompleteEnumPtr; // OK +extern enum incomplete_enum* __counted_by(external_enum_len) incompleteEnumPtr2; // OK + +int global_enum_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBEnumPtrImplicitInit' with type 'enum incomplete_enum *__single __counted_by(global_enum_len)' (aka 'enum incomplete_enum *__single') because the pointee type 'enum incomplete_enum' is incomplete; consider providing a complete definition for 'enum incomplete_enum' before this definition or using the '__sized_by' attribute}} +enum incomplete_enum* __counted_by(global_enum_len) GlobalCBEnumPtrImplicitInit; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONEnumPtrImplicitInit' with type 'enum incomplete_enum *__single __counted_by_or_null(global_enum_len)' (aka 'enum incomplete_enum *__single') because the pointee type 'enum incomplete_enum' is incomplete; consider providing a complete definition for 'enum incomplete_enum' before this definition or using the '__sized_by_or_null' attribute}} +enum incomplete_enum* __counted_by_or_null(global_enum_len) GlobalCBONEnumPtrImplicitInit; + + +// Unions are handled like structs for the diagnostics so the testing for structs +// should mean testing other combinations should be unnecessary. diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c new file mode 100644 index 0000000000000..cb73d76670334 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c @@ -0,0 +1,1443 @@ +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +#include +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type. + +// NOTE: For a typedef the source location is of the underlying type instead of +// the typedef. This seems like the right behavior because the typedef isn't the +// forward declaration, `struct IncompleteStructTy` is. +// +// expected-note@+1 62{{forward declaration of 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy')}} +struct IncompleteStructTy; // expected-note 153{{forward declaration of 'struct IncompleteStructTy'}} + +typedef struct IncompleteStructTy Incomplete_Struct_t; + +//------------------------------------------------------------------------------ +// Attribute on parameters +//------------------------------------------------------------------------------ + +// On declarations its ok to use the attribute +void no_consume_ok( + struct IncompleteStructTy* __counted_by(size) cb, + struct IncompleteStructTy* __counted_by_or_null(size) cbon, + int size); // OK + +// Using the attribute on parameters on a function **definition** is not allowed. +void no_consume_ok( + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'cb' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(size) cb, + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'cbon' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(size) cbon, + int size) { + +} + +void no_consume_ok_unnamed_param( + struct IncompleteStructTy* __counted_by(size), + struct IncompleteStructTy* __counted_by_or_null(size), + int size); // OK + +void no_consume_ok_unnamed_param( + // expected-error@+2{{cannot apply '__counted_by' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + struct IncompleteStructTy* __counted_by(size), + // expected-error@+2{{cannot apply '__counted_by_or_null' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + struct IncompleteStructTy* __counted_by_or_null(size), + int size) { + +} + +void consume_cb(struct IncompleteStructTy* __counted_by(size_cb), int size_cb); +void consume_cbon(struct IncompleteStructTy* __counted_by_or_null(size_cbon), int size_cbon); + +void consume_param_read_write( + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'cb' with type 'struct IncompleteStructTy *__single __counted_by(size_cb)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(size_cb) cb, + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'cbon' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size_cbon)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(size_cbon) cbon, + int size_cb, + int size_cbon) { + + // There shouldn't be diagnostics on the uses because the parameters are marked + // as invalid. + + // Read + struct IncompleteStructTy* local = cb; + local = cb; + local =&cb[1]; + consume_cb(cb); + + // Write + cb = 0x0; + // TODO: This diagnostic should not be firing. rdar://133001202 + // expected-error@+1{{assignment to 'size_cb' requires corresponding assignment to 'struct IncompleteStructTy *__single __counted_by(size_cb)' (aka 'struct IncompleteStructTy *__single') 'cb'; add self assignment 'cb = cb' if the value has not changed}} + size_cb = 0; + + + // Read + struct IncompleteStructTy* local2 = cbon; + local2 = cbon; + local2 =&cbon[1]; + consume_cbon(cbon); + + // Write + cbon = 0x0; + // TODO: This diagnostic should not be firing. rdar://133001202 + // expected-error@+1{{assignment to 'size_cbon' requires corresponding assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(size_cbon)' (aka 'struct IncompleteStructTy *__single') 'cbon'; add self assignment 'cbon = cbon' if the value has not changed}} + size_cbon = 0; +} + +// These errors seem prevent emitting any further diagnostics about the attributes. +void no_consume_default_assign( + // expected-error@+1{{C does not support default arguments}} + struct IncompleteStructTy* __counted_by(size) cb = 0x0, + // expected-error@+1{{C does not support default arguments}} + struct IncompleteStructTy* __counted_by_or_null(size) cbon = 0x0, + int size) { + +} + +//------------------------------------------------------------------------------ +// Attribute on parameters with nested attributes +//------------------------------------------------------------------------------ +void consume_param_nested( + struct IncompleteStructTy* __counted_by(size1)* cb, // expected-note 3{{__counted_by attribute is here}} + struct IncompleteStructTy* __counted_by_or_null(size2)* cbon, // expected-note 3{{__counted_by_or_null attribute is here}} + int size1, int size2) { + + // Surprisingly `&cb[0]` doesn't count as a use. + struct IncompleteStructTy** local = &cb[0]; + + // expected-error@+1{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + local = cb; + + // expected-error@+1{{cannot assign to object that has type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + *cb = 0x0; + size1 = 0; + + // expected-error@+1{{cannot use 'cb[0]' with type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + void* read_cb = cb[0]; + // expected-error@+1{{cannot use '*cb' with type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_cb(*cb, size1); + + // expected-error@+1{{not allowed to change out parameter with dependent count}} + cb = 0x0; + + // Surprisingly `&cbon[0]` doesn't count as a use. + struct IncompleteStructTy** local2 = &cbon[0]; + // expected-error@+1{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + local = cbon; + + // expected-error@+1{{cannot assign to object that has type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + *cbon = 0x0; + size2 = 0; + + // expected-error@+1{{cannot use 'cbon[0]' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + void* read_cbon = cbon[0]; + // expected-error@+1{{cannot use '*cbon' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_cbon(*cbon, size2); + + // expected-error@+1{{not allowed to change out parameter with dependent count}} + cbon = 0x0; +} + +//------------------------------------------------------------------------------ +// Attribute on return type of called function +//------------------------------------------------------------------------------ + +// expected-note@+1{{__counted_by attribute is here}} +struct IncompleteStructTy* __counted_by(size) ret_cb_IncompleteStructTy(int size); // OK +// expected-note@+1{{__counted_by attribute is here}} +Incomplete_Struct_t* __counted_by(size) ret_cb_IncompleteStructTy_typedef(int size); // OK +// expected-note@+1{{__counted_by_or_null attribute is here}} +struct IncompleteStructTy* __counted_by_or_null(size) ret_cbon_IncompleteStructTy(int size); // OK +// expected-note@+1{{__counted_by_or_null attribute is here}} +Incomplete_Struct_t* __counted_by_or_null(size) ret_cbon_IncompleteStructTy_typedef(int size); // OK + +// expected-note@+1{{__counted_by attribute is here}} +struct IncompleteStructTy* __counted_by(1) ret_cb_IncompleteStructTy_const_count_one(void); // OK +// expected-note@+1{{__counted_by attribute is here}} +Incomplete_Struct_t* __counted_by(1) ret_cb_IncompleteStructTy_typedef_const_count_one(void); // OK +// expected-note@+1{{__counted_by_or_null attribute is here}} +struct IncompleteStructTy* __counted_by_or_null(1) ret_cbon_IncompleteStructTy_const_count_one(void); // OK +// expected-note@+1{{__counted_by_or_null attribute is here}} +Incomplete_Struct_t* __counted_by_or_null(1) ret_cbon_IncompleteStructTy_typedef_const_count_one(void); // OK + +void call_fn_returns_incomplete_pointee(void) { + int size = 0; + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy' with return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + ret_cb_IncompleteStructTy(size); + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_typedef' with return type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + ret_cb_IncompleteStructTy_typedef(size); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy' with return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + ret_cbon_IncompleteStructTy(size); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_typedef' with return type 'Incomplete_Struct_t *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + ret_cbon_IncompleteStructTy_typedef(size); + + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_const_count_one' with return type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + ret_cb_IncompleteStructTy_const_count_one(); + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_typedef_const_count_one' with return type 'Incomplete_Struct_t *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + ret_cb_IncompleteStructTy_typedef_const_count_one(); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_const_count_one' with return type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + ret_cbon_IncompleteStructTy_const_count_one(); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_typedef_const_count_one' with return type 'Incomplete_Struct_t *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + ret_cbon_IncompleteStructTy_typedef_const_count_one(); +} + +//------------------------------------------------------------------------------ +// Attribute on return type in function declaration +//------------------------------------------------------------------------------ + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) // expected-note{{__counted_by attribute is here}} + consume_param_and_return_cb(int size) { + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) + consume_param_and_return_cb_missing_return(int size) { + // missing return statement +} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(size) + consume_param_and_return_cbon_missing_return(int size) { + // missing return statement +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) // expected-note 2{{__counted_by attribute is here}} + consume_param_and_return_cb_multiple_returns(int size) { + if (size == 0) + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return 0x0; + + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 2{{__counted_by_or_null attribute is here}} + consume_param_and_return_cbon_multiple_returns(int size) { + if (size == 0) + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return 0x0; + + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(1) // expected-note{{__counted_by attribute is here}} + consume_param_and_return_cb_const_count_1(int size) { + // rs-error@+2{{returning null from a function with result type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 always fails}} + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) // expected-note{{__counted_by attribute is here}} +consume_param_and_return_cb_single_forge(int size) { + // rs-warning@+2{{count value is not statically known: returning 'struct IncompleteStructTy *__single' from a function with result type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') is invalid for any count other than 0 or 1}} + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return __unsafe_forge_single(struct IncompleteStructTy*, 0x0); +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) // expected-note{{__counted_by attribute is here}} + consume_param_and_return_cb_single_forge_bidi(int size) { + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x0, 4); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note{{__counted_by_or_null attribute is here}} + consume_param_and_return_cbon(int size) { + // TODO: We should consider allowing this because the assignment of nullptr + // means the type size isn't needed (rdar://129424354). + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note{{__counted_by_or_null attribute is here}} +consume_param_and_return_cbon_single_forge(int size) { + // rs-warning@+2{{count value is not statically known: returning 'struct IncompleteStructTy *__single' from a function with result type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') is invalid for any count other than 0 or 1 unless the pointer is null}} + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return __unsafe_forge_single(struct IncompleteStructTy*, 0x0); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note{{__counted_by_or_null attribute is here}} + consume_param_and_return_cbon_single_forge_bidi(int size) { + // expected-error@+1{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x0, 4); +} + +// Test typedef as the incomplete pointee type +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before the function body or using the '__sized_by' attribute}} +Incomplete_Struct_t* __counted_by(size) // expected-note{{__counted_by attribute is here}} + consume_param_and_return_cb_typedef(int size) { + // expected-error@+1{{cannot return type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + return 0x0; +} + +// Check Incomplete type diagnostic and bad conversion diagnostics both emitted on return + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(size) // expected-note{{__counted_by attribute is here}} + consume_param_and_return_cb_bad_conversion(int size) { + // expected-error@+2{{cannot return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return 0x1; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribut}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note{{__counted_by_or_null attribute is here}} + consume_param_and_return_cbon_bad_conversion(int size) { + // expected-error@+2{{cannot return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return 0x1; +} + +//------------------------------------------------------------------------------ +// Pass Arguments to parameters with attribute. +//------------------------------------------------------------------------------ +void consume_incomplete_cb(struct IncompleteStructTy* __counted_by(size) c, int size); // expected-note{{__counted_by attribute is here}} +void consume_incomplete_cb_unnamed(struct IncompleteStructTy* __counted_by(size), int size); // expected-note{{__counted_by attribute is here}} +typedef void consume_incomplete_cb_t(struct IncompleteStructTy* __counted_by(size) c, int size); // expected-note{{__counted_by attribute is here}} + +void call_consume_incomplete_cb(consume_incomplete_cb_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' that has type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb_unnamed(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); +} + +void consume_incomplete_cb_const_count_1(struct IncompleteStructTy* __counted_by(1) c); // expected-note{{__counted_by attribute is here}} +void consume_incomplete_cb_unnamed_const_count_1(struct IncompleteStructTy* __counted_by(1)); // expected-note{{__counted_by attribute is here}} +typedef void consume_incomplete_cb_const_count_1_t(struct IncompleteStructTy* __counted_by(1) c); // expected-note{{__counted_by attribute is here}} + + +void call_consume_incomplete_cb_const_count_1(consume_incomplete_cb_const_count_1_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb_const_count_1(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb_unnamed_const_count_1(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); + + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); +} + +void consume_incomplete_cbon(struct IncompleteStructTy* __counted_by_or_null(size) c, int size); // expected-note{{__counted_by_or_null attribute is here}} +void consume_incomplete_cbon_unnamed(struct IncompleteStructTy* __counted_by_or_null(size), int size); // expected-note{{__counted_by_or_null attribute is here}} +typedef void consume_incomplete_cbon_t(struct IncompleteStructTy* __counted_by_or_null(size) c, int size); // expected-note{{__counted_by_or_null attribute is here}} + +void call_consume_incomplete_cbon(consume_incomplete_cbon_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_incomplete_cbon(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_incomplete_cbon_unnamed(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + + // expected-error@+1{{cannot pass argument to parameter that has type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); +} + + +// expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'c' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} +void wrap_consume_incomplete_cb(struct IncompleteStructTy* __counted_by(size) c, + int size, consume_incomplete_cb_t indirect_call) { + // TODO: We should consider allowing this case. rdar://132031085 + // + // This case technically doesn't require any bounds-checks because: + // + // 1. We assume the attribute on `c` is correct because we assume that the + // caller to `wrap_consume_incomplete_cb` already performed bounds-checks. + // 2. uses of `c` and `size` in this function just pass them along to + // functions with the same kind of count expression. This means the bounds + // checks performed at the call to `wrap_consume_incomplete_cb` being true + // imply that all the bounds checks that would be performed here should + // pass. One exception to this would be if the call to + // `wrap_consume_incomplete_cb` was performed from non-bounds-safety code. + // + consume_incomplete_cb(c, size); + consume_incomplete_cb_unnamed(c, size); + indirect_call(c, size); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'c' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} +void wrap_consume_incomplete_cbon(struct IncompleteStructTy* __counted_by_or_null(size) c, + int size, consume_incomplete_cbon_t indirect_call) { + // TODO: We should consider allowing this case. rdar://132031085 + // + // This case technically doesn't require any bounds-checks because: + // + // 1. We assume the attribute on `c` is correct because we assume that the + // caller to `wrap_consume_incomplete_cb` already performed bounds-checks. + // 2. uses of `c` and `size` in this function just pass them along to + // functions with the same kind of count expression. This means the bounds + // checks performed at the call to `wrap_consume_incomplete_cb` being true + // imply that all the bounds checks that would be performed here should + // pass. One exception to this would be if the call to + // `wrap_consume_incomplete_cb` was performed from non-bounds-safety code. + // + consume_incomplete_cbon(c, size); + consume_incomplete_cbon_unnamed(c, size); + indirect_call(c, size); +} + +//------------------------------------------------------------------------------ +// Passing to Parameters with attributes on nested pointer +//------------------------------------------------------------------------------ +void consume_incomplete_cb_nested(struct IncompleteStructTy* __counted_by(*size)* out, int* size); +void consume_incomplete_cb_unnamed_nested(struct IncompleteStructTy* __counted_by(*size)*, int* size); +typedef void consume_incomplete_cb_nested_t(struct IncompleteStructTy* __counted_by(*size)* c, int* size); + +struct PtrAndCountCB { + int size; + struct IncompleteStructTy* __counted_by(size) ptr; +}; + +extern int cb_global_count; +extern struct IncompleteStructTy* __counted_by(cb_global_count) cb_global; + +void call_consume_incomplete_cb_nested(consume_incomplete_cb_nested_t indirect_call, struct PtrAndCountCB* ptr_and_count) { + // Note: + // * Uses of `&cb_global` `&(ptr_and_count->ptr)` currently don't generate errors + // because only the outer most + // pointer is checked. + // * Calls to the functions don't generate errors because only the outer most + // pointer is checked in the parameter types. + // + // This is ok because currently we don't checks at call sites to functions + // with indirect __counted_by parameters. Therefore the size of + // `struct IncompleteStructTy` isn't needed. + consume_incomplete_cb_nested(&cb_global, &cb_global_count); + consume_incomplete_cb_unnamed_nested(&cb_global, &cb_global_count); + indirect_call(&cb_global, &cb_global_count); + consume_incomplete_cb_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + consume_incomplete_cb_unnamed_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + indirect_call(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK +} + +void consume_incomplete_cbon_nested(struct IncompleteStructTy* __counted_by_or_null(*size)* out, int* size); +void consume_incomplete_cbon_unnamed_nested(struct IncompleteStructTy* __counted_by_or_null(*size)*, int* size); +typedef void consume_incomplete_cbon_nested_t(struct IncompleteStructTy* __counted_by_or_null(*size)* c, int* size); + +extern int cbon_global_count; +extern struct IncompleteStructTy* __counted_by_or_null(cbon_global_count) cbon_global; + +struct PtrAndCountCBON { + int size; + struct IncompleteStructTy* __counted_by_or_null(size) ptr; +}; + +void call_consume_incomplete_cbon_nested(consume_incomplete_cbon_nested_t indirect_call, struct PtrAndCountCBON* ptr_and_count) { + // Note: + // * Uses of `&cbon_global` `&(ptr_and_count->ptr)` currently don't generate errors + // because only the outer most + // pointer is checked. + // * Calls to the functions don't generate errors because only the outer most + // pointer is checked in the parameter types. + // + // This is ok because currently we don't checks at call sites to functions + // with indirect __counted_by parameters. Therefore the size of + // `struct IncompleteStructTy` isn't needed. + consume_incomplete_cbon_nested(&cbon_global, &cbon_global_count); + consume_incomplete_cbon_unnamed_nested(&cbon_global, &cbon_global_count); + indirect_call(&cbon_global, &cbon_global_count); + consume_incomplete_cbon_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + consume_incomplete_cbon_unnamed_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + indirect_call(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK +} + +//------------------------------------------------------------------------------ +// __counted_by/__counted_by_or_null on struct members +//------------------------------------------------------------------------------ + +struct BuffersCBTyNotUsed { + int count; + struct IncompleteStructTy* __counted_by(count) buffer; // OK + Incomplete_Struct_t* __counted_by(count) buffer_typedef; // OK +}; +struct BuffersCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count) buffer; // expected-note 21{{__counted_by attribute is here}} + Incomplete_Struct_t* __counted_by(count_typedef) buffer_typedef; // expected-note 21{{__counted_by attribute is here}} +}; + +void side_effect(void); + +void AssignToBuffersCBTy(struct BuffersCBTy* b) { + // Check that the diagnostic about missing assignment to the count also shows + + // expected-error@+2{{cannot assign to 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') 'b->buffer' requires corresponding assignment to 'b->count'; add self assignment 'b->count = b->count' if the value has not changed}} + b->buffer = 0x0; + side_effect(); + // expected-error@+2{{cannot assign to 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + // expected-error@+1{{assignment to 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') 'b->buffer_typedef' requires corresponding assignment to 'b->count_typedef'; add self assignment 'b->count_typedef = b->count_typedef' if the value has not changed}} + b->buffer_typedef = 0x0; + + // Diagnostic about missing assignment to count should not appear. + side_effect(); + b->count = 0; + // expected-error@+1{{cannot assign to 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + b->buffer = 0x0; + side_effect(); + // expected-error@+1{{cannot assign to 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + b->buffer_typedef = 0x0; + b->count_typedef = 0; +} + + +struct IncompleteStructTy* ReturnBufferCBTyMember(struct BuffersCBTy* b) { + // expected-error@+1{{cannot use 'b->buffer' with type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + return b->buffer; +} + +Incomplete_Struct_t* ReturnBufferCBTyMemberTypeDef(struct BuffersCBTy* b) { + // expected-error@+1{{cannot use 'b->buffer_typedef' with type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + return b->buffer_typedef; +} + +struct BuffersCBONTyNotUsed { + int count; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; // OK + Incomplete_Struct_t* __counted_by_or_null(count) buffer_typedef; // OK +}; +struct BuffersCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; // expected-note 21{{__counted_by_or_null attribute is here}} + Incomplete_Struct_t* __counted_by_or_null(count_typedef) buffer_typedef; // expected-note 21{{__counted_by_or_null attribute is here}} +}; + +void AssignToBuffersCBONTy(struct BuffersCBONTy* b) { + // Check that the diagnostic about missing assignment to the count also shows + + // expected-error@+2{{cannot assign to 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') 'b->buffer' requires corresponding assignment to 'b->count'; add self assignment 'b->count = b->count' if the value has not changed}} + b->buffer = 0x0; + side_effect(); + // expected-error@+2{{cannot assign to 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + // expected-error@+1{{assignment to 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') 'b->buffer_typedef' requires corresponding assignment to 'b->count_typedef'; add self assignment 'b->count_typedef = b->count_typedef' if the value has not changed}} + b->buffer_typedef = 0x0; + + // Diagnostic about missing assignment to count should not appear. + side_effect(); + b->count = 0; + // expected-error@+1{{cannot assign to 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + b->buffer = 0x0; + side_effect(); + // expected-error@+1{{cannot assign to 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + b->buffer_typedef = 0x0; + b->count_typedef = 0; +} + +struct IncompleteStructTy* ReturnBufferCBONTyMember(struct BuffersCBONTy* b) { + // expected-error@+1{{cannot use 'b->buffer' with type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + return b->buffer; +} + +Incomplete_Struct_t* ReturnBufferCBONTyMemberTypeDef(struct BuffersCBONTy* b) { + // expected-error@+1{{cannot use 'b->buffer_typedef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + return b->buffer_typedef; +} + +//------------------------------------------------------------------------------ +// Initialization of struct members with counted_by/counted_by_or_null +//------------------------------------------------------------------------------ + +// TODO: We should consider allowing implicit and explicit zero initialization +// of __counted_by_or_null pointers. rdar://129424354 + +struct BufferCBNonZeroConstCountTy { + int extra_field; + struct IncompleteStructTy* __counted_by(1) ptr; // expected-note 4{{__counted_by attribute is here}} +}; + +struct BufferCBNonZeroConstCountFlippedFieldOrderTy { + struct IncompleteStructTy* __counted_by(1) ptr; // expected-note 2{{__counted_by attribute is here}} + int extra_field; +}; + +struct BufferCBNonZeroDynCountTy { + unsigned int count; + struct IncompleteStructTy* __counted_by(count+1) ptr; // expected-note 5{{__counted_by attribute is here}} +}; + +union BufferCBOrOther { + struct BuffersCBTy buf; + int other; +}; + +void InitBuffersCBTy(int size) { + // Designated initializers + struct BuffersCBTy desig_init_0 = { + .count = size, + .count_typedef = size, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + .buffer_typedef = 0x0 + }; + + struct BuffersCBTy desig_init_1 = { + // .count and .count_typedef not explicitly initialized but are implicitly zero initialized + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + .buffer_typedef = 0x0 + }; + + struct BuffersCBTy desig_init_partial = { + .count = size, + .count_typedef = size, + // .buffer and .buffer_typedef are not explicit initialized but are implicitly zero initialized + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + struct BuffersCBTy implicit_all_zero_init = {0}; // Implicit field init + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + // non-designated initializer + struct BuffersCBTy non_design_init_0 = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + 0x0 + }; + + struct BuffersCBTy non_design_init_1 = { 0, 0 }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + struct BuffersCBTy desig_init_invalid_count = { + .count = 1, + // expected-error@+2{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'desig_init_invalid_count.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .buffer = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + struct BuffersCBTy desig_init_invalid_count_partial = { + .count = 1 + }; + // expected-error@-1{{implicitly initializing 'desig_init_invalid_count_partial.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-3{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + struct BuffersCBTy non_desig_init_invalid_count = { + 1, + 0, + // expected-error@+2{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'non_desig_init_invalid_count.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + 0x0 + }; + + + struct BuffersCBTy non_desig_init_invalid_count_partial = {1}; + // expected-error@-1{{implicitly initializing 'non_desig_init_invalid_count_partial.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-3{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + // Cases where zero-init would create an invalid count + struct BufferCBNonZeroConstCountTy design_init_const_count = { + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'design_init_const_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0, + .extra_field = 0 + }; + + struct BufferCBNonZeroConstCountTy design_init_const_count_partial_explicit = { + // .ptr is implicitly zero initialized + .extra_field = 0x0 + }; + // expected-error@-1{{implicitly initializing 'design_init_const_count_partial_explicit.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + + struct BufferCBNonZeroConstCountTy implicit_all_zero_init_const_count = {0}; + // expected-error@-1{{implicitly initializing 'implicit_all_zero_init_const_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + + // When the ptr comes first it's seen as an explicit assignment when we write ` = {0}` so we get the incomplete pointee type error diagnostic + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountFlippedFieldOrderTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'implicit_all_zero_init_const_count_ptr_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + struct BufferCBNonZeroConstCountFlippedFieldOrderTy implicit_all_zero_init_const_count_ptr_init = {0}; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count = { + .count = 0x0, + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'design_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 + }; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init = { + // count is implicitly zero + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'design_init_non_zero_dyn_count_partial_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 + }; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init2 = { + // ptr is implicitly zero initialized + .count = 0x0 + }; + // expected-error@-1{{implicitly initializing 'design_init_non_zero_dyn_count_partial_init2.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + + struct BufferCBNonZeroDynCountTy implicit_all_zero_init_non_zero_dyn_count = {0}; + // expected-error@-1{{implicitly initializing 'implicit_all_zero_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + + struct BufferCBNonZeroDynCountTy non_desig_init_non_zero_dyn_count = { + 0, + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'non_desig_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + 0 + }; + + // Struct inside a union + union BufferCBOrOther UnionDesignInitOther = {.other = 0x0 }; + union BufferCBOrOther UnionZeroInit = {0}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + union BufferCBOrOther UnionDesignInitBufZeroInitStructFields = {.buf = {0}}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + union BufferCBOrOther UnionDesignInitBufDesignInitStructFields = {.buf = {.count = 0, .buffer = 0x0}}; + // expected-error@-1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} +} + +struct BuffersCBTy GlobalBuffersCBTy_design_init = { + .count = 0, + .count_typedef = 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + .buffer_typedef = 0 +}; + +struct BuffersCBTy GlobalBuffersCBTy_non_design_init = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + 0 +}; + +struct BuffersCBTy GlobalBuffersCBTy_design_init_partial = { + .count = 0, + .count_typedef = 0 + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + +struct BuffersCBTy GlobalBuffersCBTy_non_design_init_partial = { + 0, + 0, + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + +struct BuffersCBTy GlobalBuffersCBTy_all_zero_init = {0}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by' attribute}} + + +// expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountFlippedFieldOrderTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} +// expected-error@+1{{initializing 'GlobalBuffersCBTy_implicit_all_zero_init_const_count_ptr_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +struct BufferCBNonZeroConstCountFlippedFieldOrderTy GlobalBuffersCBTy_implicit_all_zero_init_const_count_ptr_init = {0}; + +struct BufferCBNonZeroConstCountTy GlobalBuffersCBTy_const_non_zero_count = { + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'GlobalBuffersCBTy_const_non_zero_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 +}; + +// counted_by_or_null variants + + +union BufferCBONOrOther { + struct BuffersCBONTy buf; + int other; +}; + +struct BufferCBONNonZeroConstCountTy { + int extra_field; + struct IncompleteStructTy* __counted_by_or_null(1) ptr; // expected-note 4{{__counted_by_or_null attribute is here}} +}; + +struct BufferCBONNonZeroConstCountFlippedFieldOrderTy { + struct IncompleteStructTy* __counted_by_or_null(1) ptr; // expected-note 2{{__counted_by_or_null attribute is here}} + int extra_field; +}; + +struct BufferCBONNonZeroDynCountTy { + unsigned int count; + struct IncompleteStructTy* __counted_by_or_null(count+1) ptr; // expected-note 5{{__counted_by_or_null attribute is here}} +}; + +void InitBuffersCBONTy(int size) { + // Designated initializers + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_0 = { + .count = size, + .count_typedef = size, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + .buffer_typedef = 0x0 + }; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_1 = { + // .count and .count_typedef not explicitly initialized but are implicitly zero initialized + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + .buffer_typedef = 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_partial = { + .count = size, + .count_typedef = size, + // .buffer and .buffer_typedef are not explicit initialized but are implicitly zero initialized + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy implicit_all_zero_init = {0}; // Implicit field init + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // non-designated initializer + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_design_init_0 = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_design_init_1 = { 0, 0 }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // TODO: Explicit and implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_invalid_count = { + .count = 1, + // expected-error@+2{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + .buffer = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_explicit_non_zero_count_partial = { + .count = 1 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_desig_init_invalid_count = { + 1, + 0, + // expected-error@+2{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_desig_init_non_zerocount_partial = {1}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + // Cases where zero-init would create an invalid count + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy design_init_const_count = { + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + .ptr = 0x0, + .extra_field = 0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy design_init_const_count_partial_explicit = { + // .ptr is implicitly zero initialized + .extra_field = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy implicit_all_zero_init_const_count = {0}; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + // When the ptr comes first it's seen as an explicit assignment when we write ` = {0}` so we get the incomplete pointee type error diagnostic + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountFlippedFieldOrderTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + struct BufferCBONNonZeroConstCountFlippedFieldOrderTy implicit_all_zero_init_const_count_ptr_init = {0}; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count = { + .count = 0x0, + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + .ptr = 0x0 + }; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init = { + // count is implicitly zero + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + .ptr = 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init2 = { + // ptr is implicitly zero initialized + .count = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy implicit_all_zero_init_non_zero_dyn_count = {0}; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy non_desig_init_non_zero_dyn_count = { + 0, + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + 0 + }; + + // Struct inside a union + union BufferCBONOrOther UnionDesignInitOther = {.other = 0x0 }; + union BufferCBONOrOther UnionZeroInit = {0}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + union BufferCBONOrOther UnionDesignInitBufZeroInitStructFields = {.buf = {0}}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + union BufferCBONOrOther UnionDesignInitBufDesignInitStructFields = {.buf = {.count = 0, .buffer = 0x0}}; + // expected-error@-1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} +} + +// XXX + +struct BuffersCBONTy GlobalBuffersCBONTy_design_init = { + .count = 0, + .count_typedef = 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + .buffer_typedef = 0 +}; + +struct BuffersCBONTy GlobalBuffersCBONTy_non_design_init = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + 0 +}; + +struct BuffersCBONTy GlobalBuffersCBONTy_design_init_partial = { + .count = 0, + .count_typedef = 0 + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + +struct BuffersCBONTy GlobalBuffersCBONTy_non_design_init_partial = { + 0, + 0, + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + +struct BuffersCBONTy GlobalBuffersCBONTy_all_zero_init = {0}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' that has type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'Incomplete_Struct_t' or using the '__sized_by_or_null' attribute}} + + +// expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountFlippedFieldOrderTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} +// +struct BufferCBONNonZeroConstCountFlippedFieldOrderTy GlobalBuffersCBONTy_implicit_all_zero_init_const_count_ptr_init = {0}; + +struct BufferCBONNonZeroConstCountTy GlobalBuffersCBONTy_const_non_zero_count = { + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountTy::ptr' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // + .ptr = 0x0 +}; + +//------------------------------------------------------------------------------ +// Local __counted_by variables +//------------------------------------------------------------------------------ +void local_cb_init_and_assign(int s) { + int size = s; + + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(size) local_init = 0x0; + local_init = 0x0; + + int implicit_size = s; + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'implicit_init' with type 'struct IncompleteStructTy *__single __counted_by(implicit_size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(implicit_size) implicit_init; + implicit_init = 0x0; +} + +void local_cb_init_and_assign_constant_count(void) { + // Check we also emit diagnostics about assigning nullptr to `__counted_by(X)` where X > 0 + // + // expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} + // expected-error@+1{{initializing 'local_init' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} + struct IncompleteStructTy* __counted_by(5) local_init = 0x0; + local_init = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // There should be no diagnostic about assigning nullptr + // TODO: We should consider allowing this given that the type size isn't + // really needed when the count is 0 (rdar://129424147). + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init_zero' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(0) local_init_zero = 0x0; + local_init_zero = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init2' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} + struct IncompleteStructTy* __counted_by(5) local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); + local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // Diagnostic suppressed because the VarDecl is invalid +} + +void local_cbon_init_and_assign(int s) { + int size = s; + + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(size) local_init = 0x0; + local_init = 0x0; + + int implicit_size = s; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'implicit_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(implicit_size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(implicit_size) implicit_init; + implicit_init = 0x0; +} + +void local_cbon_init_and_assign_constant_count(void) { + // TODO: We should consider allowing this because the assignment of nullptr + // means the type size isn't needed (rdar://129424354). + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(5) local_init = 0x0; + local_init = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // There should be no diagnostic about assigning nullptr + // TODO: We should consider allowing this given that the type size isn't + // really needed when the count is 0 (rdar://129424147). + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init_zero' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(0) local_init_zero = 0x0; + local_init_zero = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init2' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteStructTy* __counted_by_or_null(5) local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); + local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // Diagnostic suppressed because the VarDecl is invalid +} + +//------------------------------------------------------------------------------ +// Global __counted_by variables +//------------------------------------------------------------------------------ +// NOTE: Tentative definitions are mostly tested in `counted_by_type_incomplete_completable_struct_tentative_defs.c`. + +extern int external_count; +// expected-note@+1 3{{__counted_by attribute is here}} +extern struct IncompleteStructTy* __counted_by(external_count) GlobalCBPtrToIncompleteTy; // OK +extern Incomplete_Struct_t* __counted_by(external_count) GlobalCBPtrToIncompleteTyTypeDef; // OK + +void use_GlobalCBPtrToIncompleteTy(void) { + // expected-error@+2{{cannot assign to 'GlobalCBPtrToIncompleteTy' that has type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBPtrToIncompleteTy' requires corresponding assignment to 'external_count'; add self assignment 'external_count = external_count' if the value has not changed}} + GlobalCBPtrToIncompleteTy = 0x0; + // expected-error@+1{{cannot use 'GlobalCBPtrToIncompleteTy' with type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + GlobalCBPtrToIncompleteTy[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBPtrToIncompleteTy' with type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb(GlobalCBPtrToIncompleteTy, external_count); +} + +static int global_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +static struct IncompleteStructTy* __counted_by(global_count) GlobalCBPtrImplicitInit; // expected-note 3{{__counted_by attribute is here}} +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +static Incomplete_Struct_t* __counted_by(global_count) GlobalCBPtrImplicitInitTypeDef; + +void use_GlobalCBPtrImplicitInit(void) { + // expected-error@+2{{cannot assign to 'GlobalCBPtrImplicitInit' that has type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBPtrImplicitInit' requires corresponding assignment to 'global_count'; add self assignment 'global_count = global_count' if the value has not changed}} + GlobalCBPtrImplicitInit = 0x0; + // expected-error@+1{{cannot use 'GlobalCBPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + GlobalCBPtrImplicitInit[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_incomplete_cb(GlobalCBPtrImplicitInit, global_count); +} + +int global_count_non_static = 0; +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(global_count_non_static) GlobalCBPtrExplicitInit = 0x0; +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(global_count_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +Incomplete_Struct_t* __counted_by(global_count_non_static) GlobalCBPtrExplicitInitTypeDef = 0x0; + +void use_GlobalCBPtrExplicitInit(void) { + // No diagnostics because the VarDecl is marked as invalid at this point + GlobalCBPtrExplicitInit = 0x0; + GlobalCBPtrExplicitInit[0] = 0; + consume_incomplete_cb(GlobalCBPtrExplicitInit, global_count_non_static); +} + +// This is very unidiomatic C but it seems to be legal. +// expected-warning@+1{{'extern' variable has an initializer}} +extern int global_count_extern = 0; +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitExtern' with type 'struct IncompleteStructTy *__single __counted_by(global_count_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribut}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern struct IncompleteStructTy* __counted_by(global_count_extern) GlobalCBPtrExplicitInitExtern = 0x0; +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitTypeDefExtern' with type 'Incomplete_Struct_t *__single __counted_by(global_count_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern Incomplete_Struct_t* __counted_by(global_count_extern) GlobalCBPtrExplicitInitTypeDefExtern = 0x0; + +// TODO: We should consider allowing this given that the pointee type size isn't +// really needed when the count is 0 (rdar://129424147) +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(0) GlobalCBPtrImplicitInitConstantZeroCount; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +Incomplete_Struct_t* __counted_by(0) GlobalCBPtrImplicitInitConstantZeroCountTypeDef; +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy* __counted_by(0) GlobalCBPtrExplicitInitConstantZeroCount = 0x0; +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +Incomplete_Struct_t* __counted_by(0) GlobalCBPtrExplicitInitConstantZeroCountTypeDef = 0x0; + +// expected-error@+2{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +// expected-error@+1{{implicitly initializing 'GlobalCBPtrImplicitInitConstantNonZeroCount' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +struct IncompleteStructTy* __counted_by(5) GlobalCBPtrImplicitInitConstantNonZeroCount; +// expected-error@+2{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +// expected-error@+1{{implicitly initializing 'GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef' of type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +Incomplete_Struct_t* __counted_by(5) GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef; +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +// expected-error@+1{{initializing 'GlobalCBPtrExplicitInitConstantNonZeroCount' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +struct IncompleteStructTy* __counted_by(5) GlobalCBPtrExplicitInitConstantNonZeroCount = 0x0; +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +// expected-error@+1{{initializing 'GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef' of type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +Incomplete_Struct_t* __counted_by(5) GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef = 0x0; + +//------------------------------------------------------------------------------ +// Global __counted_by_or_null variables +//------------------------------------------------------------------------------ +// NOTE: Tentative definitions are mostly tested in `counted_by_type_incomplete_completable_struct_tentative_defs.c`. + +extern int external_count_cbon; +extern int external_count_cbon_typedef; +// expected-note@+1 3{{__counted_by_or_null attribute is here}} +extern struct IncompleteStructTy* __counted_by_or_null(external_count_cbon) GlobalCBONPtrToIncompleteTy; // OK +extern Incomplete_Struct_t* __counted_by_or_null(external_count_cbon_typedef) GlobalCBONPtrToIncompleteTyTypeDef; // OK + +void use_GlobalCBONPtrToIncompleteTy(void) { + // expected-error@+2{{cannot assign to 'GlobalCBONPtrToIncompleteTy' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBONPtrToIncompleteTy' requires corresponding assignment to 'external_count_cbon'; add self assignment 'external_count_cbon = external_count_cbon' if the value has not changed}} + GlobalCBONPtrToIncompleteTy = 0x0; + // expected-error@+1{{cannot use 'GlobalCBONPtrToIncompleteTy' with type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + GlobalCBONPtrToIncompleteTy[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBONPtrToIncompleteTy' with type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_incomplete_cbon(GlobalCBONPtrToIncompleteTy, external_count); +} + + + +static int global_count_cbon; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +static struct IncompleteStructTy* __counted_by_or_null(global_count_cbon) GlobalCBONPtrImplicitInit; // expected-note 3{{__counted_by_or_null attribute is here}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +static Incomplete_Struct_t* __counted_by_or_null(global_count_cbon) GlobalCBONPtrImplicitInitTypeDef; + + + +void use_GlobalCBONPtrImplicitInit(void) { + // expected-error@+2{{cannot assign to 'GlobalCBONPtrImplicitInit' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBONPtrImplicitInit' requires corresponding assignment to 'global_count_cbon'; add self assignment 'global_count_cbon = global_count_cbon' if the value has not changed}} + GlobalCBONPtrImplicitInit = 0x0; + // expected-error@+1{{cannot use 'GlobalCBONPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + GlobalCBONPtrImplicitInit[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBONPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_incomplete_cbon(GlobalCBONPtrImplicitInit, global_count_cbon); +} + + + +int global_count_cbon_non_static = 0; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(global_count_cbon_non_static) GlobalCBONPtrExplicitInit = 0x0; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t* __counted_by_or_null(global_count_cbon_non_static) GlobalCBONPtrExplicitInitTypeDef = 0x0; + +void use_GlobalCBONPtrExplicitInit(void) { + // No diagnostics because the VarDecl is marked as invalid at this point + GlobalCBONPtrExplicitInit = 0x0; + GlobalCBONPtrExplicitInit[0] = 0; + consume_incomplete_cbon(GlobalCBONPtrExplicitInit, global_count_cbon_non_static); +} + + + +// This is very unidiomatic C but it seems to be legal. +// expected-warning@+1{{'extern' variable has an initializer}} +extern int global_count_cbon_extern = 0; +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitExtern' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern struct IncompleteStructTy* __counted_by_or_null(global_count_cbon_extern) GlobalCBONPtrExplicitInitExtern = 0x0; +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitTypeDefExtern' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribut}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern Incomplete_Struct_t* __counted_by_or_null(global_count_cbon_extern) GlobalCBONPtrExplicitInitTypeDefExtern = 0x0; + + +// TODO: We should consider allowing this given that the pointee type size isn't +// really needed when the count is 0 (rdar://129424147) +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribut}} +struct IncompleteStructTy* __counted_by_or_null(0) GlobalCBONPtrImplicitInitConstantZeroCount; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t* __counted_by_or_null(0) GlobalCBONPtrImplicitInitConstantZeroCountTypeDef; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(0) GlobalCBONPtrExplicitInitConstantZeroCount = 0x0; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t* __counted_by_or_null(0) GlobalCBONPtrExplicitInitConstantZeroCountTypeDef = 0x0; + +// Unlike `__counted_by` assigning 0x0 (implicitly or explicitly) is allowed for `__counted_by_or_null` +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(5) GlobalCBONPtrImplicitInitConstantNonZeroCount; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t* __counted_by_or_null(5) GlobalCBONPtrImplicitInitConstantNonZeroCountTypeDef; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy* __counted_by_or_null(5) GlobalCBONPtrExplicitInitConstantNonZeroCount = 0x0; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t* __counted_by_or_null(5) GlobalCBONPtrExplicitInitConstantNonZeroCountTypeDef = 0x0; + +//------------------------------------------------------------------------------ +// No explicit forward decl +//------------------------------------------------------------------------------ + +// expected-note@+1 2{{forward declaration of 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl')}} +typedef struct NoExplicitForwardDecl NoExplicitForwardDecl_t; + +extern NoExplicitForwardDecl_t* __counted_by(0) NoExplicitForwardDeclGlobalCBPtr; // expected-note{{__counted_by attribute is here}} +void consume_NoExplicitForwardDeclGlobalCBPtr(void) { + // expected-error@+1{{cannot assign to 'NoExplicitForwardDeclGlobalCBPtr' that has type 'NoExplicitForwardDecl_t *__single __counted_by(0)' (aka 'struct NoExplicitForwardDecl *__single') because the pointee type 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl') is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'NoExplicitForwardDecl_t' or using the '__sized_by' attribute}} + NoExplicitForwardDeclGlobalCBPtr = 0x0; +} + +extern NoExplicitForwardDecl_t* __counted_by_or_null(0) NoExplicitForwardDeclGlobalCBONPtr; // expected-note{{__counted_by_or_null attribute is here}} +void consume_NoExplicitForwardDeclGlobalCBONPtr(void) { + // expected-error@+1{{cannot assign to 'NoExplicitForwardDeclGlobalCBONPtr' that has type 'NoExplicitForwardDecl_t *__single __counted_by_or_null(0)' (aka 'struct NoExplicitForwardDecl *__single') because the pointee type 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl') is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'NoExplicitForwardDecl_t' or using the '__sized_by_or_null' attribute}} + NoExplicitForwardDeclGlobalCBONPtr = 0x0; +} + +//------------------------------------------------------------------------------ +// Array element initialization +// +// Currently this appears to be forbidden +//------------------------------------------------------------------------------ + +void array_elts_init_cb(void) { + int size; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + struct IncompleteStructTy*__counted_by(size) arr[2] = { 0x0, 0x0 }; +} + +void array_elts_init_cbon(void) { + int size; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + struct IncompleteStructTy*__counted_by_or_null(size) arr[2] = { 0x0, 0x0 }; +} + +//------------------------------------------------------------------------------ +// Casting +//------------------------------------------------------------------------------ +// TODO: These should cause errors to be emitted. +// rdar://131621712 +void explicit_cast_cb_to_single(struct IncompleteStructTy* p) { + struct IncompleteStructTy* __single tmp = + (struct IncompleteStructTy* __counted_by(1)) p; +} + +void explicit_cast_cbon_to_single(struct IncompleteStructTy* p) { + struct IncompleteStructTy* __single tmp = + (struct IncompleteStructTy* __counted_by_or_null(1)) p; +} + + +void explicit_cast_cb_to_bidi(struct IncompleteStructTy* p) { + // TODO: This diagnostic is misleading. It says __single but it should probably be `__counted_by(2)`. rdar://133002045 + // expected-error@+1{{cannot initialize indexable pointer with type 'struct IncompleteStructTy *__bidi_indexable' from __single pointer to incomplete type 'struct IncompleteStructTy *__single'; consider declaring pointer 'local_bidi' as '__single'}} + struct IncompleteStructTy* local_bidi = (struct IncompleteStructTy* __counted_by(2)) p; // expected-note{{pointer 'local_bidi' declared here}} +} + +void explicit_cast_cbon_to_bidi(struct IncompleteStructTy* p) { + // TODO: This diagnostic is misleading. It says __single but it should probably be `__counted_by(2)`. rdar://133002045 + // expected-error@+1{{cannot initialize indexable pointer with type 'struct IncompleteStructTy *__bidi_indexable' from __single pointer to incomplete type 'struct IncompleteStructTy *__single'; consider declaring pointer 'local_bidi' as '__single'}} + struct IncompleteStructTy* local_bidi = (struct IncompleteStructTy* __counted_by_or_null(2)) p; // expected-note{{pointer 'local_bidi' declared here}} +} + +//------------------------------------------------------------------------------ +// Completing the pointee type allows usage +//------------------------------------------------------------------------------ + +// expected-note@+1 20{{forward declaration of 'struct IncompleteLaterCompletedStructTy'}} +struct IncompleteLaterCompletedStructTy; + +// Confirm using the type is an error at this point +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExpectErr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrExpectErr = 0x0; +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrTentativeDefUseWillErr; // expected-note{{__counted_by attribute is here}} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExpectErr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) GlobalCBONPtrExpectErr = 0x0; +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) GlobalCBONPtrTentativeDefUseWillErr; // expected-note{{__counted_by_or_null attribute is here}} + +struct StructPtrIncompleteLaterCompleted { + int count; + struct IncompleteLaterCompletedStructTy*__counted_by(count) ptr; // expected-note 2{{__counted_by attribute is here}} +}; +struct StructCBONPtrIncompleteLaterCompleted { + int count; + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(count) ptr; // expected-note 2{{__counted_by_or_null attribute is here}} +}; + +void consume_IncompleteLaterCompletedStructTy(struct IncompleteLaterCompletedStructTy*__counted_by(0) p); // expected-note{{__counted_by attribute is here}} +void consume_CBON_IncompleteLaterCompletedStructTy(struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) p); // expected-note{{__counted_by_or_null attribute is here}} + +struct IncompleteLaterCompletedStructTy*__counted_by(0) ret_IncompleteLaterCompletedStructTy(void); // expected-note{{__counted_by attribute is here}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) ret_CBON_IncompleteLaterCompletedStructTy(void); // expected-note{{__counted_by_or_null attribute is here}} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before the function body or using the '__sized_by' attribute}} +struct IncompleteLaterCompletedStructTy*__counted_by(0) test_cb_expect_err( // expected-note{{__counted_by attribute is here}} + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'param' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before the function body or using the '__sized_by' attribute}} + struct IncompleteLaterCompletedStructTy*__counted_by(0) param) { + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before this definition or using the '__sized_by' attribute}} + struct IncompleteLaterCompletedStructTy*__counted_by(0) local; + + // expected-error@+1{{cannot assign to 'GlobalCBPtrTentativeDefUseWillErr' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + GlobalCBPtrTentativeDefUseWillErr = 0; + + // expected-error@+1{{annot initialize 'StructPtrIncompleteLaterCompleted::ptr' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + struct StructPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + + struct StructPtrIncompleteLaterCompleted tmp2; + // expected-error@+1{{cannot use 'tmp2.ptr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + consume_IncompleteLaterCompletedStructTy(tmp2.ptr); + + // expected-error@+1{{cannot pass argument to parameter 'p' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + consume_IncompleteLaterCompletedStructTy(0x0); + + // expected-error@+1{{cannot call 'ret_IncompleteLaterCompletedStructTy' with return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + ret_IncompleteLaterCompletedStructTy(); + + // expected-error@+1{{cannot return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by' attribute}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before the function body or using the '__sized_by_or_null' attribute}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) test_cbon_expect_err( // expected-note{{__counted_by_or_null attribute is here}} + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'param' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before the function body or using the '__sized_by_or_null' attribute}} + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) param) { + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' before this definition or using the '__sized_by_or_null' attribute}} + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) local; + + // expected-error@+1{{cannot assign to 'GlobalCBONPtrTentativeDefUseWillErr' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + GlobalCBONPtrTentativeDefUseWillErr = 0; + + // expected-error@+1{{cannot initialize 'StructCBONPtrIncompleteLaterCompleted::ptr' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when initializing; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + struct StructCBONPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + + struct StructCBONPtrIncompleteLaterCompleted tmp2; + // expected-error@+1{{cannot use 'tmp2.ptr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + consume_CBON_IncompleteLaterCompletedStructTy(tmp2.ptr); + + // expected-error@+1{{cannot pass argument to parameter 'p' that has type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when passing; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + consume_CBON_IncompleteLaterCompletedStructTy(0x0); + + // expected-error@+1{{cannot call 'ret_CBON_IncompleteLaterCompletedStructTy' with return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + ret_CBON_IncompleteLaterCompletedStructTy(); + + // expected-error@+1{{cannot return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when returning; consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy' or using the '__sized_by_or_null' attribute}} + return 0x0; +} + + +// Now complete the type and confirm it can be used +struct IncompleteLaterCompletedStructTy { + int field; +}; + +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrExpectNoErr = 0x0; + +struct IncompleteLaterCompletedStructTy*__counted_by(0) test_cb_expect_no_err( + struct IncompleteLaterCompletedStructTy*__counted_by(0) param) { + struct IncompleteLaterCompletedStructTy*__counted_by(0) local; + + GlobalCBPtrTentativeDefUseWillErr = 0; + + struct StructPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + struct StructPtrIncompleteLaterCompleted tmp2; + consume_IncompleteLaterCompletedStructTy(tmp2.ptr); + + consume_IncompleteLaterCompletedStructTy(0x0); + ret_IncompleteLaterCompletedStructTy(); + + return 0x0; +} + +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) test_cbon_expect_no_err( + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) param) { + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) local; + + GlobalCBONPtrTentativeDefUseWillErr = 0; + + struct StructCBONPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + struct StructCBONPtrIncompleteLaterCompleted tmp2; + consume_CBON_IncompleteLaterCompletedStructTy(tmp2.ptr); + + consume_CBON_IncompleteLaterCompletedStructTy(0x0); + ret_CBON_IncompleteLaterCompletedStructTy(); + + return 0x0; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c new file mode 100644 index 0000000000000..3e7e3f5c328da --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +//------------------------------------------------------------------------------ +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type on block parameters/return type +//------------------------------------------------------------------------------ + +struct IncompleteStructTy; // expected-note 6{{forward declaration of 'struct IncompleteStructTy'}} + +//------------------------------------------------------------------------------ +// Attribute on block parameters +//------------------------------------------------------------------------------ + +typedef void(^cb_block_fn_t)(struct IncompleteStructTy* __counted_by(size), int size); // OK +typedef void(^cbon_block_fn_t)(struct IncompleteStructTy* __counted_by_or_null(size), int size); // OK + +void consume_cb(struct IncompleteStructTy* __counted_by(size), int size); +void consume_cbon(struct IncompleteStructTy* __counted_by_or_null(size), int size); + +void use_block_params_cb(void) { + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + cb_block_fn_t f_named = ^(struct IncompleteStructTy* __counted_by(size) buf, int size) { + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; + + // expected-error@+2{{cannot apply '__counted_by' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + cb_block_fn_t f_unnamed = ^(struct IncompleteStructTy* __counted_by(size), int size) { + }; + + void (^f_var_no_typedef_decl)(struct IncompleteStructTy* __counted_by(size), int size) = + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by' attribute}} + ^(struct IncompleteStructTy* __counted_by(size) buf, int size) { + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; +} + +void use_block_params_cbon(void) { + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + cbon_block_fn_t f_named = ^(struct IncompleteStructTy* __counted_by_or_null(size) buf, int size) { + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; + + // expected-error@+2{{cannot apply '__counted_by_or_null' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + cbon_block_fn_t f_unnamed = ^(struct IncompleteStructTy* __counted_by_or_null(size), int size) { + }; + + void (^f_var_no_typedef_decl)(struct IncompleteStructTy* __counted_by_or_null(size), int size) = + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before the function body or using the '__sized_by_or_null' attribute}} + ^(struct IncompleteStructTy* __counted_by_or_null(size) buf, int size) { + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; +} + +//------------------------------------------------------------------------------ +// Attribute on block return type +//------------------------------------------------------------------------------ + +// TODO: We should probably lift this restriction. rdar://132927574 +// expected-error@+1{{'__counted_by' inside typedef is only allowed for function type}} +typedef struct IncompleteStructTy* __counted_by(size)(^cb_block_ret_fn_t)(int size); + +// Don't test this because it causes clang to crash. rdar://132927229 +// void try_block_ret(void) { +// struct IncompleteStructTy*__counted_by(size) (^f_var_no_typedef_decl)(int size) = ^ struct IncompleteStructTy*__counted_by(size) (int size) { + +// }; +// } diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c new file mode 100644 index 0000000000000..ec1bbd8bed72f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c @@ -0,0 +1,287 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type **and** that involve tentative definitions. + +// NOTE: For a typedef the source location is of the underlying type instead of +// the typedef. This seems like the right behavior because the typedef isn't the +// forward declaration, `struct IncompleteStructTy` is. +// +// expected-note@+2 2{{forward declaration of 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy')}} +// expected-note@+1 30{{forward declaration of 'struct IncompleteStructTy'}} +struct IncompleteStructTy; + +//------------------------------------------------------------------------------ +// Only one tentative definition +//------------------------------------------------------------------------------ + +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_one' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_one; // expected-note{{__counted_by attribute is here}} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_one' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_one; // expected-note{{__counted_by_or_null attribute is here}} + +void use_cb_t_dfn_one(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_one' that has type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_one = 0x0; +} + +void use_cbon_t_dfn_one(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_one' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_one = 0x0; +} + +//------------------------------------------------------------------------------ +// Two tentative definitions +//------------------------------------------------------------------------------ + +// We only error on the last one which is treated as a definition once the whole +// TU has been processed. + +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_two; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_two' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_two; // expected-note{{__counted_by attribute is here}} + +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_two; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_two' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_two; // expected-note{{__counted_by_or_null attribute is here}} + +void use_cb_t_dfn_two(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_two' that has type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_two = 0x0; +} + +void use_cbon_t_dfn_two(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_two' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_two = 0x0; +} + +//------------------------------------------------------------------------------ +// Definition followed by tentative definition +//------------------------------------------------------------------------------ + +// NOTE: The diagnostic about initializing `cb_t_dfn_after_def` is suppressed. + +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'cb_t_dfn_after_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def = 0x0; +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def; // OK - tentative definition acts like declaration +// expected-note@+1{{__counted_by attribute is here}} +extern struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def; // OK - declaration + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'cbon_t_dfn_after_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def = 0x0; +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def; // OK - tentative definition acts like declaration +// expected-note@+1{{__counted_by_or_null}} +extern struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def; // OK - declaration + +void use_cb_t_dfn_after_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_after_def' that has type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_after_def = 0x0; +} + +void use_cbon_t_dfn_after_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_after_def' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_after_def = 0x0; +} + +//------------------------------------------------------------------------------ +// Definition preceeded by tentative definition +//------------------------------------------------------------------------------ + +// NOTE: The diagnostic about initializing `cb_t_dfn_after_def` is suppressed. +// NOTE: This test case is the **only** case where diagnostics about variable use +// are suppressed due the variable decl being invalid. So it does more testing +// than other `use_*` functions in this file. + +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def; // OK - tentative definition acts like declaration +// expected-note@+1 2{{__counted_by attribute is here}} +extern struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def; // OK - declaration + +void consume_cb_const_count_zero(struct IncompleteStructTy*__counted_by(0) p); + +void use_cb_t_dfn_before_def_before_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_before_def' that has type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_before_def = 0x0; + + // expected-error@+1{{cannot use 'cb_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + consume_cb_const_count_zero(cb_t_dfn_before_def); +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'cb_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def = 0x0; + +void use_cb_t_dfn_before_def_after_def(void) { + // no error here. At this point `cb_t_dfn_before_def` has been marked as invalid + // because we've seen a definition and have marked the variable as invalid. + cb_t_dfn_before_def = 0x0; + consume_cb_const_count_zero(cb_t_dfn_before_def); +} + +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def; // OK - tentative definition acts like declaration +// expected-note@+1 2{{__counted_by_or_null attribute is here}} +extern struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def; // OK - declaration + +void consume_cbon_const_count_zero(struct IncompleteStructTy*__counted_by_or_null(0) p); + +void use_cbon_t_dfn_before_def_before_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_before_def' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_before_def = 0x0; + + // expected-error@+1{{cannot use 'cbon_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete in this context; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + consume_cbon_const_count_zero(cbon_t_dfn_before_def); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'cbon_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def = 0x0; + +void use_cbon_t_dfn_before_def_after_def(void) { + // no error here. At this point `cbon_t_dfn_before_def` has been marked as invalid + // because we've seen a definition and have marked the variable as invalid. + cbon_t_dfn_before_def = 0x0; + + consume_cbon_const_count_zero(cbon_t_dfn_before_def); +} + +//------------------------------------------------------------------------------ +// Tentative definition due to `static` keyword +//------------------------------------------------------------------------------ + +static struct IncompleteStructTy*__counted_by(0) cb_t_dfn_static_def; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_static_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +static struct IncompleteStructTy*__counted_by(0) cb_t_dfn_static_def; // expected-note{{__counted_by attribute is here}} + +static struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_static_def; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_static_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +static struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_static_def; // expected-note{{__counted_by_or_null attribute is here}} + +void use_cb_t_dfn_static_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_static_def' that has type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_static_def = 0x0; +} + +void use_cbon_t_dfn_static_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_static_def' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_static_def = 0x0; +} + +//------------------------------------------------------------------------------ +// Tentative definition and non-const count +//------------------------------------------------------------------------------ + +static int cb_t_dfn_static_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_static_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +static struct IncompleteStructTy*__counted_by(cb_t_dfn_static_non_const_count_count) cb_t_dfn_static_non_const_count; // expected-note{{__counted_by attribute is here}} + +static int cbon_t_dfn_static_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_static_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +static struct IncompleteStructTy*__counted_by_or_null(cbon_t_dfn_static_non_const_count_count) cbon_t_dfn_static_non_const_count; // expected-note{{__counted_by_or_null attribute is here}} + + +int cb_t_dfn_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(cb_t_dfn_non_const_count_count) cb_t_dfn_non_const_count; // expected-note{{__counted_by attribute is here}} + +int cbon_t_dfn_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(cbon_t_dfn_non_const_count_count) cbon_t_dfn_non_const_count; // expected-note{{__counted_by_or_null attribute is here}} + +void use_cb_t_dfn_static_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_static_non_const_count' that has type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_static_non_const_count = 0x0; + cb_t_dfn_static_non_const_count_count = 0; +} + +void use_cbon_t_dfn_static_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_static_non_const_count' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_static_non_const_count = 0x0; + cbon_t_dfn_static_non_const_count_count = 0; +} + +void use_cb_t_dfn_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_non_const_count' that has type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by' attribute}} + cb_t_dfn_non_const_count = 0x0; + cb_t_dfn_non_const_count_count = 0; +} + +void use_cbon_t_dfn_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_non_const_count' that has type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete and the '__counted_by_or_null' attribute requires the pointee type be complete when assigning; consider providing a complete definition for 'struct IncompleteStructTy' or using the '__sized_by_or_null' attribute}} + cbon_t_dfn_non_const_count = 0x0; + cbon_t_dfn_non_const_count_count = 0; +} + +//------------------------------------------------------------------------------ +// Only one tentative definition on typedef +//------------------------------------------------------------------------------ + +typedef struct IncompleteStructTy Incomplete_Struct_t; + +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_one_typedef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +Incomplete_Struct_t*__counted_by(0) cb_t_dfn_one_typedef; + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_one_typedef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t*__counted_by_or_null(0) cbon_t_dfn_one_typedef; + + +//------------------------------------------------------------------------------ +// Tentative definition using incomplete pointee that is later completed +//------------------------------------------------------------------------------ + +struct IncompleteStructLaterCompletedTy; + +// This is a tentative definition where the pointee type is incomplete. However, +// by the end of the translation unit (when we do the check) the +// pointee type has now become complete so the error diagnostic is not emitted. +struct IncompleteStructLaterCompletedTy* __counted_by(0) cb_t_dfn_later_completed; + +struct IncompleteStructLaterCompletedTy { + int field; +}; + +//------------------------------------------------------------------------------ +// Tentative definition of struct with field using __counted_by or +// __counted_by_or_null with an incomplete pointee type +//------------------------------------------------------------------------------ +struct BuffersCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count) buffer; + Incomplete_Struct_t* __counted_by(count_typedef) buffer_typedef; +}; + +struct BuffersCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; + Incomplete_Struct_t* __counted_by_or_null(count_typedef) buffer_typedef; +}; + +// TODO: Technically this is ok because zero initialization doesn't require +// a check and therefor doesn't need the pointee type size. However, it's +// inconsistent to disallow assignment via `= {0}` but allow it for tentative +// definitions that get zero initialized. rdar://133573722 +struct BuffersCBTy GlobalBuffersCBTy_implicit_init; +struct BuffersCBONTy GlobalBuffersCBONTy_implicit_init; + +struct BuffersNonZeroCountCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count+1) buffer; + Incomplete_Struct_t* __counted_by(count_typedef+1) buffer_typedef; +}; + +struct BuffersNonZeroCountCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count+1) buffer; + Incomplete_Struct_t* __counted_by_or_null(count_typedef+1) buffer_typedef; +}; + +// TODO: It's inconsistent to disallow assignment via `= {0}` but allow it for +// tentative definitions that get zero initialized. rdar://133573722 +// +// expected-error@+2{{implicitly initializing 'GlobalBuffersNonZeroCountCBTy_implicit_init.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +// expected-error@+1{{implicitly initializing 'GlobalBuffersNonZeroCountCBTy_implicit_init.buffer_typedef' of type 'Incomplete_Struct_t *__single __counted_by(count_typedef + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +struct BuffersNonZeroCountCBTy GlobalBuffersNonZeroCountCBTy_implicit_init; +struct BuffersNonZeroCountCBONTy GlobalBuffersNonZeroCountCBONTy_implicit_init; // OK diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c new file mode 100644 index 0000000000000..b6625f5b4d64f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete unions + +extern int external_union_len; +typedef union incomplete_union incomplete_union_t; // expected-note 2{{forward declaration of 'union incomplete_union'}} +extern incomplete_union_t* __counted_by(external_union_len) incompleteUnionPtr; // OK +extern union incomplete_union* __counted_by(external_union_len) incompleteUnionPtr2; // OK + +int global_union_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBUnionPtrImplicitInit' with type 'union incomplete_union *__single __counted_by(global_union_len)' (aka 'union incomplete_union *__single') because the pointee type 'union incomplete_union' is incomplete; consider providing a complete definition for 'union incomplete_union' before this definition or using the '__sized_by' attribute}} +union incomplete_union* __counted_by(global_union_len) GlobalCBUnionPtrImplicitInit; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONUnionPtrImplicitInit' with type 'union incomplete_union *__single __counted_by_or_null(global_union_len)' (aka 'union incomplete_union *__single') because the pointee type 'union incomplete_union' is incomplete; consider providing a complete definition for 'union incomplete_union' before this definition or using the '__sized_by_or_null' attribute}} +union incomplete_union* __counted_by_or_null(global_union_len) GlobalCBONUnionPtrImplicitInit; + +// Unions are handled like structs for the diagnostics so the testing for structs +// should mean testing other combinations should be unnecessary. diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c new file mode 100644 index 0000000000000..02bca4c514caf --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include + +int len; + +//------------------------------------------------------------------------------ +// void pointer +//------------------------------------------------------------------------------ +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:7-[[@LINE+2]]:19}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} +void *__counted_by(len) voidPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:7-[[@LINE+2]]:27}:"__sized_by_or_null" +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void *__counted_by_or_null(len) voidPtrOrNull; + +//------------------------------------------------------------------------------ +// Function pointer +//------------------------------------------------------------------------------ +typedef int (*fptr_t)(int*); + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:20}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'int (*)(int *__single)' because 'int (int *__single)' has unknown size; did you mean to use '__sized_by' instead?}} +fptr_t __counted_by(len) fPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:28}:"__sized_by_or_null" +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'int (*)(int *__single)' because 'int (int *__single)' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +fptr_t __counted_by_or_null(len) fPtrOrNull; + + +//------------------------------------------------------------------------------ +// variable length array +//------------------------------------------------------------------------------ +struct vla { + int size; + char data[__counted_by(size)]; +}; +typedef struct vla vla_t; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:20}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'vla_t *' (aka 'struct vla *') because 'vla_t' (aka 'struct vla') has unknown size; did you mean to use '__sized_by' instead?}} +vla_t* __counted_by(len) vlaPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:13-[[@LINE+2]]:25}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'struct vla *' because 'struct vla' has unknown size; did you mean to use '__sized_by' instead?}} +struct vla* __counted_by(len) vlaPtr2; + +//------------------------------------------------------------------------------ +// builtins +//------------------------------------------------------------------------------ +#ifdef __aarch64__ + // CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:17-[[@LINE+2]]:29}:"__sized_by" + // expected-error@+1{{cannot apply '__counted_by' attribute to '__SVInt8_t *' because '__SVInt8_t' has unknown size; did you mean to use '__sized_by' instead?}} + __SVInt8_t* __counted_by(len) countSVInt8; +#endif diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c new file mode 100644 index 0000000000000..617e10771a0ad --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include + +int len; + +// Using attribute directly should not suggest a fix-it. +// expected-error-re@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size{{$}}}} +void *__attribute__((__counted_by__(len))) voidPtr; + +// Using a custom macro that wraps the attribute should not suggest a fix-it. +// expected-error-re@+2{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size{{$}}}} +#define my_custom_counted_by(X) __attribute__((__counted_by__(X))) +void * my_custom_counted_by(len) voidPtr2; + +// CHECK-NOT: fix-it:"{{.+}}": diff --git a/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c b/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c new file mode 100644 index 0000000000000..052ef9f39976d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo(void) { + int n; + int* n_ptr1 = &n; + int* n_ptr2 = &n; + int *__counted_by(*n_ptr1) local_buf1; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} + int *__counted_by(*n_ptr2) local_buf2; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} +} diff --git a/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c b/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c new file mode 100644 index 0000000000000..f478fcf1d6fa4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int p; +// expected-warning@+1{{incompatible pointer types initializing 'int *__single' with an expression of type 'char *__bidi_indexable'}} +int *q1 = ((char*)&p) + 1; // expected-error{{initializer element is not a compile-time constant}} +int *q2 = &p; +int *q3 = &p + 1; // expected-error{{initializer element is not a compile-time constant}} + +int arr[10]; +int *q4 = arr; +int *q5 = arr + 9; +int *q6 = arr + 10; // expected-error{{initializer element is not a compile-time constant}} + +extern int iarr[]; +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +int *q7 = iarr; // expected-error{{initializer element is not a compile-time constant}} + +extern int iarr_cnt[__counted_by(10)]; +int *q8 = iarr_cnt; +int *q9 = &iarr_cnt[9]; +int *q10 = iarr_cnt + 10; // expected-error{{initializer element is not a compile-time constant}} + +int g_val; +// expected-warning@+1{{array with '__counted_by' and the argument of the attribute should be defined in the same translation unit}} +extern int iarr_cnt_val[__counted_by(g_val)]; +int *q11 = iarr_cnt_val; // expected-error{{initializer element is not a compile-time constant}} + +char c; +// expected-warning@+1{{incompatible pointer types initializing 'int *__single' with an expression of type 'char *__bidi_indexable'}} +int *q12 = &c; // expected-error{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c b/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c new file mode 100644 index 0000000000000..d76c49bfb6fe9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int glen; + +struct T { + char *buf __counted_by(glen); // expected-error{{count expression on struct field may only reference other fields of the same struct}} + int len; +}; + +struct T_flex_1 { + int dummy; + int fam[__counted_by(glen)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; + + +int glen2; +struct T_flex_2 { + int dummy; + int fam[__counted_by(glen2)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; + +void Test () { + int len; + struct S { + int *__counted_by(len) buf; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + int *__counted_by(glen) buf2; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; + + struct S_flex_1 { + int dummy; + int fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; + + struct S_flex_2 { + int dummy; + int fam[__counted_by(glen)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; +} + +void foo(int *__counted_by(glen) buf); // expected-error{{count expression in function declaration may only reference parameters of that function}} +void bar(int p[__counted_by(glen)]); // expected-error{{count expression in function declaration may only reference parameters of that function}} diff --git a/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c b/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c new file mode 100644 index 0000000000000..8c6b9a243d3f0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int side_effect(int *ptr); + +int Test (int *__counted_by(n) buf, int n, struct S s) { + // expected-note@+1{{'buf' has been assigned here}} + buf = 0; + // expected-error@+1{{cannot reference 'buf' after it is changed during consecutive assignments}} + n = side_effect(buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + + s.bp = 0; + // expected-note@+1{{previously assigned here}} + s.l = side_effect(s.bp2); + // expected-error@+2{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'l' must be simplified; keep only one of the assignments}} + s.l = 0; // expected-error{{assignments to dependent variables should not have side effects between them}} + + n = side_effect(buf); // no error the side effect (RHS) happens before the assignment + buf = 0; + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c b/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c new file mode 100644 index 0000000000000..e1997ccad7abe --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +int foo(int *__counted_by(*out_n + 1) *out_buf, int *out_n); // expected-error{{invalid argument expression to bounds attribute}} + +int bar(int *__counted_by(*out_n) *out_buf, int *out_n); + + +struct T { + int *__counted_by(l) buf; + int l; +}; + + +int main() { + int n = -1; + int *__counted_by(n + 1) buf; + // XXX: this error message is misleading because `foo` had an attribute error and + // thus the function parameter does not have the proper attribute to check. + // expected-error@+1{{passing address of 'n' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo(&buf, &n); + + bar(&buf, &n); // expected-error{{incompatible count expression (*out_n) vs. (n + 1) in argument to function}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c b/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c new file mode 100644 index 0000000000000..d769bc0ff32b7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c @@ -0,0 +1,81 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include + +int side_effect(int *buf); + +int foo(int *__counted_by(*len) *buf, int *len) { + int arr[10]; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(*len) *p; + int *__counted_by(*len) q; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} + int *other_len; + int **normal_p = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + len = other_len; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter}} + buf = normal_p; // expected-error{{not allowed to change out parameter with dependent count}} + *buf = arr; + *len = side_effect(*buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + side_effect(*buf); + *len = side_effect(*buf); + *buf = arr; + return 0; +} + +int test_inbuf_outlen(int *__counted_by(*len) buf, int *len) { + int arr[10]; + // expected-error@+2{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + // expected-error@+1{{assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') 'buf' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + buf = arr; + side_effect(0); + *len = 10; + return 0; +} + +int test_outbuf_inlen(int *__counted_by(len) *buf, int len) { + int arr[10]; + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') '*buf'; add self assignment '*buf = *buf' if the value has not changed}} + len = 10; + side_effect(0); + // expected-error@+1{{assignment to 'int *__single __counted_by(len)' (aka 'int *__single') '*buf' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} + *buf = arr; + return 0; +} + +int foo_nullable(int *__counted_by_or_null(*len) *buf, int *len) { + int arr[10]; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by_or_null(*len) *p; + int *__counted_by_or_null(*len) q; // expected-error{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + int *other_len; + int **normal_p = buf; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + len = other_len; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter}} + buf = normal_p; // expected-error{{not allowed to change out parameter with dependent count}} + *buf = arr; + *len = side_effect(*buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + side_effect(*buf); + *len = side_effect(*buf); + *buf = arr; + return 0; +} + +int test_inbuf_outlen_nullable(int *__counted_by_or_null(*len) buf, int *len) { + int arr[10]; + // expected-error@+2{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + // expected-error@+1{{assignment to 'int *__single __counted_by_or_null(*len)' (aka 'int *__single') 'buf' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + buf = arr; + side_effect(0); + *len = 10; + return 0; +} + +int test_outbuf_inlen_nullable(int *__counted_by_or_null(len) *buf, int len) { + int arr[10]; + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by_or_null(len)' (aka 'int *__single') '*buf'; add self assignment '*buf = *buf' if the value has not changed}} + len = 10; + side_effect(0); + // expected-error@+1{{assignment to 'int *__single __counted_by_or_null(len)' (aka 'int *__single') '*buf' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} + *buf = arr; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-casts.c b/clang/test/BoundsSafety/Sema/dependent-count-casts.c new file mode 100644 index 0000000000000..00cab9540c2d0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-casts.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include +// rdar://70688465 +int foo(int *__counted_by(len) *buf, int len) { + int **local_buf = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + + return 0; +} + +int bar(int *__counted_by_or_null(len) *buf, int len) { + int **local_buf = buf; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + + return 0; +} + +int main() { + int *__single buf; + foo(&buf, 10); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + bar(&buf, 10); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c b/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c new file mode 100644 index 0000000000000..540f45f919349 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +int foo(int *__counted_by(*out_n) *out_buf, int *out_n, int *no_out_n); + + +int main() { + int n; + int *__counted_by(n) buf; + int *p = 0; + int *__single plain_buf; + foo(&buf, &n, p); + foo(&buf, p, &n); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&plain_buf, &n, p); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c b/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c new file mode 100644 index 0000000000000..ed44b10804af5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c @@ -0,0 +1,157 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByData { + unsigned len; + char *__counted_by(len) buf; +}; + +void TestOrderImplOK(struct CountedByData *data) { + data->len -= 10; + data->buf += 10; +} + + +void TestOrderImplOK2(struct CountedByData *data) { + data->len = 4; + data->buf = data->buf + 10; +} + +void TestOrderOK(struct CountedByData *data) { + data->buf += 10; + data->len -= 10; +} + +void TestOrderRefOK(struct CountedByData *data) { + data->buf = data->buf + 1; + data->len -= 1; +} + +void TestOutParamOrderImplOK(char *__sized_by(*outLen) *outBuf, unsigned *outLen) { + *outLen = 10; + *outBuf += 1; +} + +void *__sized_by(l) alloc(unsigned long long l); + +void TestOrderPtrPromoteFail1(struct CountedByData *data, unsigned new_len) { + data->len = new_len; + data->buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderPtrPromoteFail2(struct CountedByData *data, unsigned new_len) { + data->len = new_len; // expected-note{{'data->len' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + data->buf = alloc(data->len); // expected-error{{cannot reference 'data->len' after it is changed during consecutive assignments}} +} + +void TestOrderPtrPromoteOK(struct CountedByData *data, unsigned new_len) { + data->buf = alloc(new_len); + data->len = new_len; +} + +unsigned glen; +char *__counted_by(glen) gbuf; + +void TestOrderGlobalPtrPromoteFail1(unsigned new_len) { + glen = new_len; + gbuf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderGlobalPtrPromoteFail2(unsigned new_len) { + glen = new_len; // expected-note{{'glen' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + gbuf = alloc(glen); // expected-error{{cannot reference 'glen' after it is changed during consecutive assignments}} +} + +void TestOrderGlobalPtrPromoteOK(unsigned new_len) { + gbuf = alloc(new_len); + glen = new_len; +} + +void TestOrderParmPtrPromoteFail1(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + len = new_len; + buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderParmPtrPromoteFail2(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + len = new_len; // expected-note{{'len' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + buf = alloc(len); // expected-error{{cannot reference 'len' after it is changed during consecutive assignments}} +} + +void TestOrderParmPtrPromoteOK(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + buf = alloc(new_len); + len = new_len; +} + +void TestOrderLocalPtrPromoteFail1(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + len = new_len; + buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderLocalPtrPromoteFail2(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + // expected-note@+1{{'len' has been assigned here}} + len = new_len; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + buf = alloc(len); // expected-error{{cannot reference 'len' after it is changed during consecutive assignments}} +} + +void TestOrderLocalPtrPromoteOK(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + buf = alloc(new_len); + len = new_len; +} + + +struct CountedByData2 { + unsigned len; + char *__counted_by(len) buf; + char *__counted_by(len) buf2; +}; + +void TestOrderRefOK2(struct CountedByData2 *data) { + data->buf2 = data->buf; // old data->buf with old data->len + data->buf = data->buf + 1; + data->len -= 1; +} + +void TestOrderRefFail(struct CountedByData2 *data) { + data->buf = data->buf + 1; // expected-note{{'data->buf' has been assigned here}} + data->buf2 = data->buf; // expected-error{{cannot reference 'data->buf' after it is changed during consecutive assignments}} + data->len -= 1; +} + +void TestOrderRefFail2(struct CountedByData2 *data) { + data->len = 10; // expected-note{{'data->len' has been assigned here}} + data->buf = data->buf + data->len; // expected-error{{cannot reference 'data->len' after it is changed during consecutive assignments}} + data->buf2 = data->buf2 + 1; +} + +struct EndedByData { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void TestEndedByImplOK(struct EndedByData *data) { + data->end -= 1; + data->iter += 1; + data->start = data->start; +} + +void TestEndedByOK(struct EndedByData *data) { + data->iter += 1; + data->end -= 1; + data->start = data->start; +} diff --git a/clang/test/BoundsSafety/Sema/disallow-union-count.c b/clang/test/BoundsSafety/Sema/disallow-union-count.c new file mode 100644 index 0000000000000..036cee5c5e00a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/disallow-union-count.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +union S { + int *p1 __counted_by(count); + // expected-error@-1 {{cannot use '__counted_by' on union fields}} + int count; + int *p2 __ended_by(end); + // expected-error@-1 {{cannot use '__ended_by' on union fields}} + int *end; + int *p3 __sized_by(size); + // expected-error@-1 {{cannot use '__sized_by' on union fields}} + int size; + struct + { + int *p1 __counted_by(count); + int count; + int *p2 __ended_by(end); + int *end; + int *p3 __sized_by(size); + int size; + } nested; + + struct + { + int *p4 __counted_by(count2); + int count2; + int *p5 __ended_by(end2); + int *end2; + int *p6 __sized_by(size2); + int size2; + } /* anonymous */; +}; diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c b/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c new file mode 100644 index 0000000000000..736fa2b9ebbab --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// expected-note@+8{{passing argument to parameter 'out_start' here}} +// expected-note@+7{{passing argument to parameter 'out_start' here}} +// expected-note@+6{{passing argument to parameter 'out_start' here}} +// expected-note@+5{{passing argument to parameter 'out_start' here}} +// expected-note@+4{{passing argument to parameter 'out_end' here}} +// expected-note@+3{{passing argument to parameter 'out_end' here}} +// expected-note@+2{{passing argument to parameter 'out_end' here}} +// expected-note@+1{{passing argument to parameter 'out_end' here}} +void callee_out_ranges(int *__ended_by(*out_end) *out_start, int **out_end); + +void caller_out_ranges_wrong_args(int *__ended_by(*out_end) *out_start, int **out_end) { + callee_out_ranges(out_start, out_end); + // XXX: rdar://97038292 + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single')}} + callee_out_ranges(out_start, *out_end); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_start', 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_start, *out_end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_start', 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_start, out_end); + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(out_end, out_start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(out_end, *out_start); + // expected-warning@+3{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_end, *out_start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_end, out_start); +} + +// expected-note@+8{{passing argument to parameter 'start' here}} +// expected-note@+7{{passing argument to parameter 'start' here}} +// expected-note@+6{{passing argument to parameter 'start' here}} +// expected-note@+5{{passing argument to parameter 'start' here}} +// expected-note@+4{{passing argument to parameter 'end' here}} +// expected-note@+3{{passing argument to parameter 'end' here}} +// expected-note@+2{{passing argument to parameter 'end' here}} +// expected-note@+1{{passing argument to parameter 'end' here}} +void callee_in_ranges(int *__ended_by(end) start, int *end); + +void caller_in_ranges_wrong_args(int *__ended_by(end) start, int *end) { + callee_in_ranges(start, end); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&start, &end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&start, end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + callee_in_ranges(start, &end); + callee_in_ranges(end, start); + // expected-warning@+3{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&end, &start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&end, start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + callee_in_ranges(end, &start); +} +// expected-note@+9{{passing argument to parameter 'out_count' here}} +// expected-note@+8{{passing argument to parameter 'out_count' here}} +// expected-note@+7{{passing argument to parameter 'out_count' here}} +// expected-note@+6{{passing argument to parameter 'out_buf' here}} +// expected-note@+5{{passing argument to parameter 'out_buf' here}} +// expected-note@+4{{passing argument to parameter 'out_buf' here}} +// expected-note@+3{{passing argument to parameter 'out_buf' here}} +// expected-note@+2{{passing argument to parameter 'out_buf' here}} +// expected-note@+1{{passing argument to parameter 'out_buf' here}} +void callee_out_count_buf(int *__counted_by(*out_count) *out_buf, int *out_count); + +void caller_out_count_wrong_args(int *__counted_by(*out_count) *out_buf, int *out_count) { + callee_out_count_buf(out_buf, out_count); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(out_buf, *out_count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)' (aka 'int *__single') to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_buf, *out_count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)' (aka 'int *__single') to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(*out_buf, out_count); + // expected-warning@+3{{incompatible pointer types passing 'int *__single' to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); take the address with &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single') to parameter of type 'int *__single'; dereference with *}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(out_count, out_buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_count, out_buf); + // expected-warning@+2{{incompatible pointer types passing 'int *__single' to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); take the address with &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(out_count, *out_buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_count, *out_buf); +} + +// expected-note@+8{{passing argument to parameter 'count' here}} +// expected-note@+7{{passing argument to parameter 'count' here}} +// expected-note@+6{{passing argument to parameter 'count' here}} +// expected-note@+5{{passing argument to parameter 'buf' here}} +// expected-note@+4{{passing argument to parameter 'buf' here}} +// expected-note@+3{{passing argument to parameter 'buf' here}} +// expected-note@+2{{passing argument to parameter 'buf' here}} +// expected-note@+1{{passing argument to parameter 'buf' here}} +void callee_in_count_buf(int *__counted_by(count) buf, int count); + +void caller_in_count_wrong_args(int *__counted_by(count) buf, int count) { + callee_in_count_buf(buf, count); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __counted_by(count)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single'); remove &}} + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__bidi_indexable' to parameter of type 'int'; remove &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single')}} + callee_in_count_buf(&buf, &count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(count)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single'); remove &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single')}} + callee_in_count_buf(&buf, count); + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__bidi_indexable' to parameter of type 'int'; remove &}} + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(buf, &count); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, *buf); + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__single __counted_by(count)' (aka 'int *__single') to parameter of type 'int'; dereference with *}} + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(&count, buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, &buf); + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(&count, *buf); +} + +// XXX: rdar://97041755 +void callee_vargs(int, ...); + +void caller_vargs_with_out_count(int *__counted_by(count) buf, int count) { + callee_vargs(1, &buf); + callee_vargs(1, &count); + callee_vargs(2, &buf, &count); + callee_vargs(2, buf, &count); + callee_vargs(2, &buf, count); + callee_vargs(2, buf, count); +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c b/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c new file mode 100644 index 0000000000000..e7c39a037c341 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c @@ -0,0 +1,146 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __counted_by + +void cb_in_in(int *__counted_by(len) buf, int len) { + int **b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int *l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l2 = len; +} + +void cb_in_out(int *__counted_by(*len) buf, int *len) { + int **b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int **l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *l2 = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l3 = *len; +} + +void cb_out_in(int *__counted_by(len) * buf, int len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int *l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l2 = len; +} + +void cb_out_out(int *__counted_by(*len) * buf, int *len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int **l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *l2 = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l3 = *len; +} + +void ptr_cb_in_in(int **__counted_by(len) buf, int len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; + int *b3 = *buf; + int b4 = **buf; +} + +void ptr_cb_out_in(int **__counted_by(len) * buf, int len) { + int ****b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b3 = *buf; + int *b4 = **buf; + int b5 = ***buf; +} + +// __sized_by +// Just check if the diagnostic says __sized_by instead of __counted_by. + +void sb_in_in(int *__sized_by(size) buf, int size) { + int **b1 = &buf; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int *s1 = &size; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int s2 = size; +} + +// __ended_by + +void eb_in_in(int *__ended_by(end) buf, int *end) { + int **b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +void eb_in_out(int *__ended_by(*end) buf, int **end) { + int **b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} + +void eb_out_in(int *__ended_by(end) * buf, int *end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +void eb_out_out(int *__ended_by(*end) * buf, int **end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} + +void ptr_eb_in_in(int **__ended_by(end) buf, int **end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; + int *b3 = *buf; + int b4 = **buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; + int *e3 = *end; + int e4 = **end; +} + +void ptr_eb_out_out(int **__ended_by(*end) * buf, int ***end) { + int ****b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b3 = *buf; + int *b4 = **buf; + int b5 = ***buf; + + int ****e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int ***e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e3 = *end; + int *e4 = **end; + int e5 = ***end; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c new file mode 100644 index 0000000000000..7fb40023e21be --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c @@ -0,0 +1,178 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int getlen(); +int side_effect(); +void *__bidi_indexable getptr(); + +struct CountedByData { + int *__counted_by(len + 1) ptr; + int len; +}; + +struct SizedByData { + void *__sized_by(len) ptr; + unsigned len; +}; + +struct CountedByOrNullData { + int *__counted_by_or_null(len + 1) ptr; + int len; +}; + +struct SizedByOrNullData { + void *__sized_by_or_null(len) ptr; + unsigned len; +}; + +struct RangedData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +struct CountedBySizedByDataMix { + int *__counted_by(len + 1) ptr; + int *__sized_by(len) ptr2; + int len; +}; + +struct InnerStruct { + int value; +}; +struct CountedByDataWithSubStruct { + int *__counted_by(len + 1) ptr; + int len; + struct InnerStruct inner; +}; + +struct CountedByDataWithSubStructAtStart { + struct InnerStruct inner; + int *__counted_by(len + 1) ptr; + int len; +}; + +struct CountedByDataWithOtherField { + int *__counted_by(len + 1) ptr; + int len; + int other_data; +}; + + + +int g[10]; + +void TestCountedBy(void) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c3 = { getptr(), 0 }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c4 = { getptr() }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c7 = { .ptr = getptr(), .len = getlen() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedBySizedByDataMix c8 = { g, g, getlen() }; + + int* ptr; + struct CountedByDataWithSubStruct c9 = {ptr, 4, {0}}; // OK + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithSubStruct c10 = {ptr, 4, {side_effect()}}; + struct CountedByDataWithSubStructAtStart c11 = {{0}, ptr, 4}; // OK + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithSubStructAtStart c12 = {{side_effect()}, ptr, 4}; + struct CountedByDataWithOtherField c13 = {ptr, 4, 0}; // OK + // expected-warning@+1{{initializer side_effect() has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithOtherField c14 = {ptr, 4, side_effect()}; +} + +void TestSizedBy(void) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s3 = { getptr(), 0 }; + // expected-warning@+2{{possibly initializing 's4.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s4 = { getptr() }; + // expected-warning@+2{{possibly initializing 's5.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s7 = { .ptr = getptr(), .len = getlen() }; +} + +void TestCountedByOrNull(void) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c3 = { getptr(), 0 }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c4 = { getptr() }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c7 = { .ptr = getptr(), .len = getlen() }; +} + +void TestSizedByOrNull(void) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s3 = { getptr(), 0 }; + // expected-warning@+2{{possibly initializing 's4.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s4 = { getptr() }; + // expected-warning@+2{{possibly initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s7 = { .ptr = getptr(), .len = getlen() }; +} + + +void TestRanged(void) { + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r1 = { getptr(), g, g }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r2 = { g, getptr(), g }; + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + struct RangedData r3 = { g, g, getptr() }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r4 = { .start = getptr(), .iter = g, .end = g }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r5 = { .start = g, .iter = getptr(), .end = g }; + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + struct RangedData r6 = { .start = g, .iter = g, .end = getptr() }; + // expected-error@+3{{initalizer for end pointer with side effects is not yet supported}} + // expected-error@+2{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r7 = { getptr(), getptr(), getptr() }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c b/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c new file mode 100644 index 0000000000000..3058248037d6e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByStruct { + int len; + int *__counted_by(len) buf; +}; + +struct EndedByStruct { + int *end; + int *__ended_by(end) iter; + int *__ended_by(iter) start; +}; + +struct CountedByStruct RetCountedByVal(void); +struct CountedByStruct *RetCountedByPtr(void); +struct EndedByStruct RetEndedByVal(void); +struct EndedByStruct *RetEndedByPtr(void); + +void Test(void) { + int *b1 = RetCountedByVal().buf; // ok + int *b2 = RetCountedByPtr()->buf; // ok + int l1 = RetCountedByVal().len; // ok + int l2 = RetCountedByPtr()->len; // ok + + int *b3 = RetEndedByVal().end; // ok + int *b4 = RetEndedByVal().iter; // ok + int *b5 = RetEndedByVal().start; // ok + + int *b6 = RetEndedByPtr()->end; // ok + int *b7 = RetEndedByPtr()->iter; // ok + int *b8 = RetEndedByPtr()->start; // ok + +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c b/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c new file mode 100644 index 0000000000000..d570947e7360c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct s_t; +extern unsigned size; +extern struct s_t * __sized_by(size) buf; + +unsigned size; +struct s_t *__sized_by(size) buf; +// expected-error@+1{{assignment to 'struct s_t *__single __sized_by(size)' (aka 'struct s_t *__single') 'buf' requires corresponding assignment to 'size'; add self assignment 'size = size' if the value has not changed}} +void assign_to_sized_by(struct s_t *__indexable p) { buf = p; } + +struct s_t; +extern struct s_t *end; +extern struct s_t * __ended_by(end) start; + +struct s_t *end; +struct s_t *__ended_by(end) start; + +// expected-error@+1{{assignment to 'struct s_t *__single __ended_by(end)' (aka 'struct s_t *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} +void assign_to_ended_by(struct s_t *__indexable p) { start = p; } +// expected-error@+1{{assignment to 'struct s_t *__single __ended_by(end)' (aka 'struct s_t *__single') 'end' requires corresponding assignment to 'start'; add self assignment 'start = start' if the value has not changed}} +void assign_to_end(struct s_t *__indexable p) { end = p; } diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c new file mode 100644 index 0000000000000..375f526e30a4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Foo(int *__counted_by(len) buf, char *dummy, int len) {} +void Bar(int len, char *dummy, int *__counted_by(len) buf) {} +void Baz(int *__counted_by(*out_len)* out_buf, char *dummy, int *out_len) {} +void Qux(int *out_len, char *dummy, int *__counted_by(*out_len)* out_buf) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + +char *side_effect_cp(); +int *__bidi_indexable side_effect_ip(); + +int Test() { + int arr[10]; + char *cp; + int len; + + Foo(arr, cp, 10); + Foo(side_effect_ip(), side_effect_cp(), 10); // ok + + Foo(side_effect_ip(), cp, len); // ok + + Foo(arr, side_effect_cp(), len); // ok + + Bar(len, side_effect_cp(), side_effect_ip()); // ok + Bar(get_len(arr), cp, arr); // ok + + int cnt; + int *__counted_by(cnt) ptr; + // Out parameters are okay because it only takes already type safe arguments. + Baz(&ptr, side_effect_cp(), &cnt); // ok + Baz(&ptr, cp, &cnt); // ok + Qux(&cnt, side_effect_cp(), &ptr); // ok + return 0; +} + +// expected-no-diagnostics \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c b/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c new file mode 100644 index 0000000000000..4358fabaad2e5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int foo(int *__counted_by(*len)* out_buf, int *len, int *dummy_len); + +int bar() { + int len; + int dummy_len; + int *__counted_by(len) ptr; + foo(&*&*&ptr, &*&len, &dummy_len); + foo(&*(&*&ptr), (&(*(int*)&len)), &dummy_len); + // switched len and dummy_len + foo(&*&ptr, &dummy_len, &*&len); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c b/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c new file mode 100644 index 0000000000000..dd054dc751777 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + +// regression test for crash + +struct S { + int * __counted_by(len) ptr; + int len; + int fam[__counted_by(len)]; +}; + +void bar(struct S * __bidi_indexable p, int * __counted_by(len) p2, int len) { + struct S * __single p3 = p; + // expected-error@+1{{use of undeclared identifier 'asdf'}} + p3->len = asdf; + p3->ptr = p2; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c new file mode 100644 index 0000000000000..b68ce041873a9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#define INT_MIN (-2147483648) + +struct S { int len; int *__counted_by(len) ptr; }; + +// expected-note@+1{{consider adding '__counted_by(2)' to 'a'}} +void TestCountedBy(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s1 = { .len = 4 }; + struct S s2 = { .len = 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's3.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s3 = { .len = -1 }; + // expected-error@+1{{initializing 's4.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s4 = { .ptr = 0, .len = 4 }; + struct S s5 = { .len = 0, .ptr = 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's6.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s6 = { .len = -1, .ptr = 0 }; + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S s7 = { 4, .ptr = arr }; + struct S s8 = { .ptr = arr, .len = 3 }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s9 = { .len = INT_MIN, .ptr = arr }; + // expected-error@+1{{initializing 's10.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + struct S s10 = { .len = 2, .ptr = a }; + struct S s11 = { .len = 1, .ptr = a }; + // expected-error@+1{{negative count value of -2147483648 for 's12.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s12 = { .len = INT_MIN, .ptr = a }; + struct S s13 = { .ptr = 0 }; // ok + // expected-warning@+1{{possibly initializing 's14.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S s14 = { .ptr = arr }; + // expected-warning@+1{{possibly initializing 's15.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S s15 = { .ptr = a }; +} + +struct U { int len; void *__sized_by(len - 1) ptr; }; + +void TestSizedByP1(char *a) { + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single') and size value of 3 with null always fails}} + struct U s1 = { .len = 4 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s2 = { .len = 0 }; + // expected-error@+1{{negative size value of -1 for 's4.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s4 = { .ptr = 0, .len = 0 }; + struct U s5 = { .len = 4, .ptr = arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s6 = { .len = 0, .ptr = arr }; + struct U s7 = { .len = 2, .ptr = a }; + struct U s8 = { .len = 1, .ptr = a }; + // expected-error@+1{{negative size value of -1 for 's9.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s9 = { .ptr = a, .len = 0 }; + // expected-error@+1{{negative size value of -1 for 's10.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s10; + // expected-error@+1{{negative size value of -1 for 's11[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s11[2]; + // expected-error@+1{{negative size value of -1 for 's12[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s12[1] = { {.len = 0} }; + struct U s13[1] = { {.len = 1} }; // ok + // expected-error@+1{{negative size value of -1 for 's14[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s14[2] = { {.len = 1} }; + + // expected-error@+1{{negative size value of -1 for 's15.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s15 = { .ptr = 0 }; + // expected-error@+1{{negative size value of -1 for 's16.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s16 = { .ptr = arr }; + // expected-error@+1{{negative size value of -1 for 's17.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s17 = { .ptr = a }; + // expected-error@+1{{negative size value of -1 for 's19[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s19[1] = { {.ptr = 0} }; + // expected-error@+2{{negative size value of -1 for 's20[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + // expected-error@+1{{negative size value of -1 for 's20[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s20[2] = { {.ptr = arr} }; +} + +struct T { void *__sized_by(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirst(void) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + // expected-error@+1{{initializing 's1.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s1 = { .ptr = 0 }; + struct T s2 = { .ptr = arr }; // ok + // expected-error@+1{{implicitly initializing 's3[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s3[4] = { {.len = -1} }; + struct T s4[1] = { {.len = -1} }; // ok + // expected-error@+1{{implicitly initializing 's5[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s5[4] = { {.len = -1, .ptr = 0} }; + struct T s6[1] = { {.len = -1, .ptr = 0} }; // ok + // expected-error@+1{{initializing 's7.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct T s7 = { .ptr = arr, 3 }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c new file mode 100644 index 0000000000000..a79fad607842d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c @@ -0,0 +1,140 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +union foo { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; +}; + +void test_foo(void) { + int arr[] = { 1, 2, 3 }; // expected-note 2{{'arr' declared here}} + + union foo f0; + + union foo f1 = {}; + + union foo f2 = { .v1 = {} }; + + union foo f3 = { .v2 = {} }; + + union foo f4 = { .v1 = { .p = arr, .len = 3 } }; + union foo f5 = { .v1 = { .p = arr, .len = 4 } }; // expected-error{{initializing 'f5.v1.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + + union foo f6 = { .v2 = { .dummy = 42, .p = arr, .len = 3 } }; + union foo f7 = { .v2 = { .dummy = 42, .p = arr, .len = 4 } }; // expected-error{{initializing 'f7.v2.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} +} + +union bar { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len + 1) p; + int len; + } v2; +}; + +void test_bar(void) { + // expected-error@+1{{implicitly initializing 'b0.v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union bar b0; + + // ok (clang picks the first field -- v1) + // TODO: Should we emit an error anyway? + union bar b1 = {}; + + union bar b2 = { .v1 = {} }; + + // expected-error@+1{{implicitly initializing 'b3.v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union bar b3 = { .v2 = {} }; +} + +union baz { + struct { + int *__counted_by(len + 1) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; +}; + +void test_baz(void) { + // expected-error@+1{{implicitly initializing 'b0.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b0; + + // expected-error@+1{{implicitly initializing 'b1.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b1 = {}; + + // expected-error@+1{{implicitly initializing 'b2.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b2 = { .v1 = {} }; + + union baz b3 = { .v2 = {} }; +} + +struct qux { + union { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len + 1) p; + int len; + } v2; + }; +}; + +void test_qux(void) { + // expected-error@+1{{implicitly initializing 'q0..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q0; + + // expected-error@+1{{implicitly initializing 'q1..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q1 = {}; + + struct qux q2 = { .v1 = {} }; + + // expected-error@+1{{implicitly initializing 'q3..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q3 = { .v2 = {} }; +} + +struct quux { + union { + struct { + int *__counted_by(len + 1) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; + }; +}; + +void test_quux(void) { + // expected-error@+1{{implicitly initializing 'q0..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q0; + + // expected-error@+1{{implicitly initializing 'q1..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q1 = {}; + + // expected-error@+1{{implicitly initializing 'q2..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q2 = { .v1 = {} }; + + struct quux q3 = { .v2 = {} }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c new file mode 100644 index 0000000000000..ef36025c054ba --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(count) ptr; + int count; +}; + +void foo(void) { + int p = 0; + struct S h = { &p }; // expected-warning{{possibly initializing 'h.ptr' of type 'int *__single __counted_by(count)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S i = { .ptr = &p }; // expected-warning{{possibly initializing 'i.ptr' of type 'int *__single __counted_by(count)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + struct S a = { 0, 0 }; // ok + struct S b = { 0 }; // ok + struct S c = { }; // ok + struct S d = { .ptr = 0, .count = 0 }; // ok + struct S e = { .ptr = 0 }; // ok + + struct S f = { &p, 0 }; // ok + struct S g = { .ptr = &p, .count = 0 }; // ok +} + +struct T { + int *__counted_by(count * 1) ptr; + int count; +}; + +void bar(void) { + int p = 0; + struct T h = { &p }; // expected-warning{{possibly initializing 'h.ptr' of type 'int *__single __counted_by(count * 1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct T i = { .ptr = &p }; // expected-warning{{possibly initializing 'i.ptr' of type 'int *__single __counted_by(count * 1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + struct T a = { 0, 0 }; // ok + struct T b = { 0 }; // ok + struct T c = { }; // ok + struct T d = { .ptr = 0, .count = 0 }; // ok + struct T e = { .ptr = 0 }; // ok + + struct T f = { &p, 0 }; // ok + struct T g = { .ptr = &p, .count = 0 }; // ok +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c new file mode 100644 index 0000000000000..9cbf386568f70 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c @@ -0,0 +1,266 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#define INT_MIN (-2147483648) + +struct S { int len; int *__counted_by(len) ptr; }; + +// expected-note@+1{{consider adding '__counted_by(2)' to 'a'}} +void TestCountedBy(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s1 = { 4 }; + struct S s2 = { 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's3.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s3 = { -1 }; + // expected-error@+1{{initializing 's4.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s4 = { 4, 0 }; + struct S s5 = { 0, 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's6.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s6 = { -1, 0 }; + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S s7 = { 4, arr }; + struct S s8 = { 3, arr }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s9 = { INT_MIN, arr }; + // expected-error@+1{{initializing 's10.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + struct S s10 = { 2, a }; + struct S s11 = { 1, a }; + // expected-error@+1{{negative count value of -2147483648 for 's12.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s12 = { INT_MIN, a }; +} + +struct V { unsigned len; void *__sized_by(len) ptr; }; + +// expected-note@+1{{consider adding '__sized_by(2)' to 'a'}} +void TestSizedBy(char *a) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with null always fails}} + struct V s1 = { 4 }; + struct V s2 = { 0 }; + // expected-error@+1{{initializing 's3.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with null always fails}} + struct V s3 = { 4, 0 }; + struct V s4 = { 0, 0 }; + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct V s5 = { 4, arr }; + struct V s6 = { 3, arr }; + // expected-error@+1{{initializing 's7.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 2 with 'char *__single' and pointee of size 1 always fails}} + struct V s7 = { 2, a }; + struct V s8 = { 1, a }; +} + +struct S2 { int len; int *__counted_by_or_null(len) ptr; }; + +void TestCountedByOrNull(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + struct S2 s1 = { 4 }; // ok + struct S2 s2 = { 0 }; // ok + struct S2 s3 = { -1 }; // ok + struct S2 s4 = { 4, 0 }; // ok + struct S2 s5 = { 0, 0 }; // ok + struct S2 s6 = { -1, 0 }; // ok + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S2 s7 = { 4, arr }; + struct S2 s8 = { 3, arr }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single')}} + struct S2 s9 = { INT_MIN, arr }; + struct S2 s10 = { 2, a }; + struct S2 s11 = { 1, a }; + // expected-error@+1{{possibly initializing 's12.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of -2147483648 with non-null; explicitly initialize null to remove this warning}} + struct S2 s12 = { INT_MIN, a }; +} + +struct V2 { unsigned len; void *__sized_by_or_null(len) ptr; }; + +void TestSizedByOrNull(char *a) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1,2,3}; + struct V2 s1 = { 4 }; // ok + struct V2 s2 = { 0 }; // ok + struct V2 s3 = { 4, 0 }; // ok + struct V2 s4 = { 0, 0 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct V2 s5 = { 4, arr }; + struct V2 s6 = { 3, arr }; + struct V2 s7 = { 2, a }; + struct V2 s8 = { 1, a }; +} + +struct U { int len; void *__sized_by(len - 1) ptr; }; + +void TestSizedByP1(char *a) { + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single') and size value of 3 with null always fails}} + struct U s1 = { 4 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s2 = { 0 }; + // expected-error@+1{{negative size value of -1 for 's4.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s4 = { 0, 0 }; + struct U s5 = { 4, arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s6 = { 0, arr }; + struct U s7 = { 2, a }; + struct U s8 = { 1, a }; + // expected-error@+1{{negative size value of -1 for 's9.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s9 = { 0, a }; + // expected-error@+1{{negative size value of -1 for 's10.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s10; + // expected-error@+1{{negative size value of -1 for 's11[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s11[2]; + // expected-error@+1{{negative size value of -1 for 's12[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s12[1] = { 0 }; + struct U s13[1] = { 1 }; // ok + // expected-error@+1{{negative size value of -1 for 's14[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s14[2] = { 1 }; +} + +struct U2 { int len; void *__sized_by_or_null(len - 1) ptr; }; + +void TestSizedByOrNullP1(char *a) { + char arr[] = {1,2,3}; + struct U2 s1 = { 4 }; // ok + struct U2 s2 = { 0 }; // ok + struct U2 s4 = { 0, 0 }; // ok + struct U2 s5 = { 4, arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single')}} + struct U2 s6 = { 0, arr }; + struct U2 s7 = { 2, a }; + struct U2 s8 = { 1, a }; + // expected-error@+1{{possibly initializing 's9.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct U2 s9 = { 0, a }; + struct U2 s10; // ok + struct U2 s11[2]; // ok + struct U2 s12[1] = { 0 }; // ok + struct U2 s13[1] = { 1 }; // ok + struct U2 s14[2] = { 1 }; // ok +} + +struct T { void *__sized_by(len - 1) ptr; int len; }; + +void TestSizedByP1PtrFirst() { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s1 = { 0 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s2 = { arr }; + // expected-error@+1{{negative size value of -1 for 's3[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s3[4] = { arr, 4}; + struct T s4[1] = { arr, 4}; // ok +} + +struct T2 { void *__sized_by_or_null(len - 1) ptr; int len; }; + +void T2estSizedByOrNullP1PtrFirst() { + char arr[] = {1, 2, 3}; + struct T2 s1 = { 0 }; // ok + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single')}} + struct T2 s2 = { arr }; + struct T2 s3[4] = { arr, 4}; // ok + struct T2 s4[1] = { arr, 4}; // ok +} + +struct W { void *__sized_by(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirst() { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + // expected-error@+1{{initializing 's1.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct W s1 = { 0 }; + struct W s2 = { arr }; + // expected-error@+1{{implicitly initializing 's3[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct W s3[4] = { 0, -1 }; + struct W s4[1] = { 0, -1 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct W s5 = { arr, 3 }; +} + +struct W2 { void *__sized_by_or_null(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirstOrNull() { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + struct W2 s1 = { 0 }; // ok + struct W2 s2 = { arr }; // ok + struct W2 s3[4] = { 0, -1 }; // ok + struct W2 s4[1] = { 0, -1 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct W2 s5 = { arr, 3 }; +} + +// TODO: rdar://76377847 +void TestAssignAfterDeclNoError(void) { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s; + s.ptr = arr; + s.len = 4; +} + +void TestAssignAfterDeclError(void) { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s; + (void)s.ptr; + s.ptr = arr; + s.len = 4; +} + +void T2estAssignAfterDeclNoErrorOrNull(void) { + char arr[] = {1, 2, 3}; + struct T2 s; + s.ptr = arr; + s.len = 4; +} + +void T2estAssignAfterDeclErrorOrNull(void) { + char arr[] = {1, 2, 3}; + struct T2 s; + (void)s.ptr; + s.ptr = arr; + s.len = 4; +} + +struct NestedS { + struct S s; +}; + +struct ReallyNestedS { + struct NestedS nested_s[2]; +}; + +// struct S has __counted_by(len), so zero-init is OK. +void TestImplicitNestedS(void) { + struct NestedS nested_s; + struct NestedS nested_s_arr[3]; + + struct ReallyNestedS really_nested_s; + struct ReallyNestedS really_nested_s_arr[3]; +} + +struct NestedU { + struct U u; +}; + +struct ReallyNestedU { + struct NestedU nested_u[2]; +}; + +// struct U has __sized_by(len - 1), so zero-init is BAD. +void TestImplicitNestedU(void) { + // expected-error@+1{{negative size value of -1 for 'nested_u.u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct NestedU nested_u; + + // expected-error@+1{{negative size value of -1 for 'nested_u_arr[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct NestedU nested_u_arr[3]; + + // expected-error@+1{{negative size value of -1 for 'really_nested_u.nested_u[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct ReallyNestedU really_nested_u; + + // expected-error@+1{{negative size value of -1 for 'really_nested_u_arr[0].nested_u[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct ReallyNestedU really_nested_u_arr[3]; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c new file mode 100644 index 0000000000000..5435448a640ec --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c @@ -0,0 +1,71 @@ +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s + +#include +#include + +void const_count_callee(int *__counted_by(10) ccp); + +int *__counted_by(10) const_count(void) { + // expected-error@+1{{initializing 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + int *__counted_by(10) cc = NULL; + + // expected-error@+1{{assigning null to 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + cc = NULL; + + // expected-error@+1{{passing null to parameter 'ccp' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + const_count_callee(NULL); + + // rs-error@+1{{returning null from a function with result type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 always fails}} + return NULL; +} + +void const_size_callee(char *__sized_by(10) csp); + +char *__sized_by(10) const_size(void) { + // expected-error@+1{{initializing 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + char *__sized_by(10) cs = NULL; + + // expected-error@+1{{assigning null to 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + cs = NULL; + + // expected-error@+1{{passing null to parameter 'csp' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + const_size_callee(NULL); + + // rs-error@+1{{returning null from a function with result type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 always fails}} + return NULL; +} + +void dynamic_count_callee(int *__counted_by(len) dcp, int len); + +void dynamic_count(void) { + // expected-error@+2{{initializing 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 10 with null always fails}} + int len = 10; + int *__counted_by(len) dc = NULL; + + // expected-error@+2{{assigning null to 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + len = 10; + dc = NULL; + + // expected-error@+1{{passing null to parameter 'dcp' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + dynamic_count_callee(NULL, 10); +} + +void dynamic_size_callee(char *__sized_by(size) dsp, int size); + +void dynamic_size(void) { + // expected-error@+2{{initializing 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') and size value of 10 with null always fails}} + int size = 10; + char *__sized_by(size) ds = NULL; + + // expected-error@+2{{assigning null to 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + size = 10; + ds = NULL; + + // expected-error@+1{{passing null to parameter 'dsp' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + dynamic_size_callee(NULL, 10); +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c new file mode 100644 index 0000000000000..e2f53866b0af1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c @@ -0,0 +1,149 @@ + + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=guarded %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=guarded %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected -fbounds-safety-bringup-missing-checks=return_size %s + +// guarded-no-diagnostics + +#include +#include + +// expected-note@+1 2{{'g_array' declared here}} +static int32_t g_array[42]; + +// __counted_by()/__sized_by() with a negative count and any pointer. + +int *__counted_by(-1) negative_cb(void) { + // expected-error@+1{{negative count value of -1 for 'int *__single __counted_by(-1)'}} + return 0; +} + +void *__sized_by(-1) negative_sb(void) { + // expected-error@+1{{negative size value of -1 for 'void *__single __sized_by(-1)'}} + return 0; +} + +// __counted_by_or_null()/__sized_by_or_null() with a negative count and nonnull ptr. + +int32_t *__counted_by_or_null(-1) negative_cbn(void) { + // expected-error@+1{{negative count value of -1 for 'int32_t *__single __counted_by_or_null(-1)'}} + return g_array; +} + +void *__sized_by_or_null(-1) negative_sbn(void) { + // expected-error@+1{{negative size value of -1 for 'void *__single __sized_by_or_null(-1)'}} + return g_array; +} + +// __counted_by()/__sized_by() with a positive count and an array. + +int32_t *__counted_by(42) array_cb_ok(void) { + return g_array; +} + +void *__sized_by(42*4) array_sb_ok(void) { + return g_array; +} + +int32_t *__counted_by(43) array_cb_bad(void) { + // expected-error-re@+1{{returning array 'g_array' (which has 42 elements) from a function with result type 'int32_t *__single __counted_by(43)'{{.*}} and count value of 43 always fails}} + return g_array; +} + +void *__sized_by(42*4+1) array_sb_bad(void) { + // expected-error-re@+1{{returning array 'g_array' (which has 168 bytes) from a function with result type 'void *__single __sized_by(169)'{{.*}} and size value of 169 always fails}} + return g_array; +} + +// __single to __counted_by()/__sized_by() with a positive count/size. + +int32_t *__counted_by(1) single_cb_ok(int32_t *__single p) { + return p; +} + +void *__sized_by(4) single_sb_ok(int32_t *__single p) { + return p; +} + +int32_t *__counted_by(2) single_cb_bad(int32_t *__single p) { + // expected-error-re@+1{{returning 'int32_t *__single'{{.*}} from a function with result type 'int32_t *__single __counted_by(2)'{{.*}} and count value of 2 always fails}} + return p; +} + +void *__sized_by(5) single_sb_bad(int32_t *__single p) { + // expected-error-re@+1{{returning 'int32_t *__single'{{.*}} with pointee of size 4 from a function with result type 'void *__single __sized_by(5)'{{.*}} and size value of 5 always fails}} + return p; +} + +// NULL to __counted_by()/__sized_by() with a positive count. + +int *__counted_by(42) null_cb(int arg) { + switch (arg) { + case 0: + // expected-error@+1{{returning null from a function with result type 'int *__single __counted_by(42)' (aka 'int *__single') and count value of 42 always fails}} + return (void*) 0; + case 1: + // TODO: This should be an error (rdar://139748843). + return (int*) 0; + } + + // expected-error@+1{{returning null from a function with result type 'int *__single __counted_by(42)' (aka 'int *__single') and count value of 42 always fails}} + return 0; +} + +int *__counted_by(size) null_cb_sized(int arg, int size) { + // No diagnostics + switch (arg) { + case 0: + return 0; + case 1: + return (void*) 0; + case 2: + return (int*) 0; + } + return 0; +} + +char *__sized_by(42) null_sb(int arg) { + switch (arg) { + case 0: + // expected-error@+1{{returning null from a function with result type 'char *__single __sized_by(42)' (aka 'char *__single') and size value of 42 always fails}} + return (void*) 0; + case 1: + // TODO: This should be an error (rdar://139748843). + return (char*) 0; + } + + // expected-error@+1{{returning null from a function with result type 'char *__single __sized_by(42)' (aka 'char *__single') and size value of 42 always fails}} + return 0; +} + +int *__sized_by(size) null_sb_sized(int arg, int size) { + // No diagnostics + switch (arg) { + case 0: + return 0; + case 1: + return (void*) 0; + case 2: + return (int*) 0; + } + return 0; +} + + +// The __counted_by_or_null()/__sized_by_or_null() pointer is set to some unknown value with a negative count/size. + +int *__counted_by_or_null(-1) negative_cbn_unknown(int *p) { + // expected-error@+1{{possibly returning non-null from a function with result type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') with count value of -1; explicitly return null to remove this warning}} + return p; +} + +void *__sized_by_or_null(-1) negative_sbn_unknown(int *p) { + // expected-error@+1{{possibly returning non-null from a function with result type 'void *__single __sized_by_or_null(-1)' (aka 'void *__single') with size value of -1; explicitly return null to remove this warning}} + return p; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c new file mode 100644 index 0000000000000..9e032d0cf5df2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo(int *__counted_by(count) p, int count) { + int *end = p + 9; + p = end; // expected-note{{'p' has been assigned here}} + count = (int)(end - p); // expected-error{{cannot reference 'p' after it is changed during consecutive assignments}} +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c new file mode 100644 index 0000000000000..bbb1c0645eea3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len) { + *len = *len - 1; +bb: + (*len)--; +} + +// Make sure that for other cases we cannot assign only len. + +void inout_buf_inout_len(int *__counted_by(*len) * out_buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void in_buf_inout_buf_inout_len(int *__counted_by(*len) buf, int *__counted_by(*len) * out_buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void inout_buf_in_buf_inout_len(int *__counted_by(*len) * out_buf, int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void in_buf_in_len(int *__counted_by(len) buf, int len) { + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'buf'; add self assignment 'buf = buf' if the value has not changed}} + len = len - 1; +bb: + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'buf'; add self assignment 'buf = buf' if the value has not changed}} + len--; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c new file mode 100644 index 0000000000000..aca1db934f7ab --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c @@ -0,0 +1,143 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +/* ------------------- + counted_by + ------------------- */ +struct foo { + int *__counted_by(len) p; + int *__counted_by(len) q; + int len; +}; + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len); + +void inout_buf_inout_len(int *__counted_by(*len) * buf, int *len) { + inout_buf_inout_len(buf, len); + + in_buf_inout_len(*buf, len); +} + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len) { + in_buf_inout_len(buf, len); + + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_len(&buf, len); +} + +void in_buf_inout_buf_inout_len(int *__counted_by(*len) p, int *__counted_by(*len) * q, int *len) { + // expected-error@+1{{passing 'len' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __counted_by(*len)*__single' (aka 'int *__single*__single'), refers to 'len'}} + in_buf_inout_len(p, len); + + // expected-error@+1{{passing 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(*len)' (aka 'int *__single'), refers to 'len'}} + inout_buf_inout_len(q, len); + + // expected-error@+1{{parameter 'p' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_len(*q, &p, len); +} + +void bar(void) { + int array[3] = {1, 2, 3}; + + int len = 3; + int *__counted_by(len) p = array; + + int non_related_len = 3; + + struct foo f; + f.len = 3; + f.p = array; + f.q = array; + + in_buf_inout_len(p, &len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(0, &len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(f.p, &f.len); + + in_buf_inout_buf_inout_len(f.p, &f.q, &f.len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(array, &f.len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(array, &len); + + in_buf_inout_len(array, &non_related_len); + + in_buf_inout_len(f.p, &non_related_len); +} + +/* ------------------- + counted_by_or_null + ------------------- */ +struct foo_nullable { + int *__counted_by_or_null(size) p; + int *__counted_by_or_null(size) q; + int size; +}; + +void in_buf_inout_size_nullable(int *__counted_by_or_null(*size) buf, int *size); + +void inout_buf_inout_size_nullable(int *__counted_by_or_null(*size) * buf, int *size) { + inout_buf_inout_size_nullable(buf, size); + + in_buf_inout_size_nullable(*buf, size); +} + +void in_buf_inout_size_nullable(int *__counted_by_or_null(*size) buf, int *size) { + in_buf_inout_size_nullable(buf, size); + + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size_nullable(&buf, size); +} + +void in_buf_inout_buf_inout_size_nullable(int *__counted_by_or_null(*size) p, int *__counted_by_or_null(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __counted_by_or_null(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size_nullable(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size_nullable(q, size); + + // expected-error@+1{{parameter 'p' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size_nullable(*q, &p, size); +} + +void bar_nullable(void) { + int array[3] = {1, 2, 3}; + + int size = 3; + int *__counted_by_or_null(size) p = array; + + int non_related_size = 12; + + struct foo_nullable f; + f.size = 3; + f.p = array; + f.q = array; + + in_buf_inout_size_nullable(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(f.p, &f.size); + + in_buf_inout_buf_inout_size_nullable(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &size); + + in_buf_inout_size_nullable(array, &non_related_size); + + in_buf_inout_size_nullable(f.p, &non_related_size); + +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c new file mode 100644 index 0000000000000..68a0e0bf178b0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c @@ -0,0 +1,144 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +/* ------------------- + sized_by + ------------------- */ +struct foo { + int *__sized_by(size) p; + int *__sized_by(size) q; + int size; +}; + +void in_buf_inout_size(int *__sized_by(*size) buf, int *size); + +void inout_buf_inout_size(int *__sized_by(*size) * buf, int *size) { + inout_buf_inout_size(buf, size); + + in_buf_inout_size(*buf, size); +} + +void in_buf_inout_size(int *__sized_by(*size) buf, int *size) { + in_buf_inout_size(buf, size); + + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size(&buf, size); +} + +void in_buf_inout_buf_inout_size(int *__sized_by(*size) p, int *__sized_by(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __sized_by(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size(q, size); + + // expected-error@+1{{parameter 'p' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size(*q, &p, size); +} + +void bar(void) { + int array[3] = {1, 2, 3}; + + int size = 12; + int *__sized_by(size) p = array; + + int non_related_size = 12; + + struct foo f; + f.size = 12; + f.p = array; + f.q = array; + + in_buf_inout_size(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(f.p, &f.size); + + in_buf_inout_buf_inout_size(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(array, &size); + + in_buf_inout_size(array, &non_related_size); + + in_buf_inout_size(f.p, &non_related_size); + +} + +/* ------------------- + sized_by_or_null + ------------------- */ +struct foo_nullable { + int *__sized_by_or_null(size) p; + int *__sized_by_or_null(size) q; + int size; +}; + +void in_buf_inout_size_nullable(int *__sized_by_or_null(*size) buf, int *size); + +void inout_buf_inout_size_nullable(int *__sized_by_or_null(*size) * buf, int *size) { + inout_buf_inout_size_nullable(buf, size); + + in_buf_inout_size_nullable(*buf, size); +} + +void in_buf_inout_size_nullable(int *__sized_by_or_null(*size) buf, int *size) { + in_buf_inout_size_nullable(buf, size); + + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size_nullable(&buf, size); +} + +void in_buf_inout_buf_inout_size_nullable(int *__sized_by_or_null(*size) p, int *__sized_by_or_null(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __sized_by_or_null(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size_nullable(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size_nullable(q, size); + + // expected-error@+1{{parameter 'p' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size_nullable(*q, &p, size); +} + +void bar_nullable(void) { + int array[3] = {1, 2, 3}; + + int size = 12; + int *__sized_by_or_null(size) p = array; + + int non_related_size = 12; + + struct foo_nullable f; + f.size = 12; + f.p = array; + f.q = array; + + in_buf_inout_size_nullable(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(f.p, &f.size); + + in_buf_inout_buf_inout_size_nullable(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &size); + + in_buf_inout_size_nullable(array, &non_related_size); + + in_buf_inout_size_nullable(f.p, &non_related_size); + +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c new file mode 100644 index 0000000000000..a62f1a7a734cc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo_c(int *__counted_by(*len) buf, int *len) { + buf = buf; + *len = *len - 1; +} + +void bar_c(int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *len = *len - 1; +} + +void baz_c(int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + buf++; + *len = *len - 1; +} + +void foo_s(int *__sized_by(*size) buf, int *size) { + buf = buf; + *size = *size - 4; +} + +void bar_s(int *__sized_by(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *size = *size - 4; +} + +void baz_s(int *__sized_by(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only}} + buf++; + *size = *size - 4; +} + +void foo_cn(int *__counted_by_or_null(*len) buf, int *len) { + buf = buf; + *len = *len - 1; +} + +void bar_cn(int *__counted_by_or_null(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *len = *len - 1; +} + +void baz_cn(int *__counted_by_or_null(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf++; + *len = *len - 1; +} + +void foo_sn(int *__sized_by_or_null(*size) buf, int *size) { + buf = buf; + *size = *size - 4; +} + +void bar_sn(int *__sized_by_or_null(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *size = *size - 4; +} + +void baz_sn(int *__sized_by_or_null(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf++; + *size = *size - 4; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c b/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c new file mode 100644 index 0000000000000..9d3aaf2eb0935 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void Test(void) { + char arr[10]; + struct S s_order_all = {arr, arr, arr + 10}; + struct S s_implicit_all; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial1 = { arr }; + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial2 = { arr, arr+1 }; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial3 = { .start = arr }; + // expected-warning@+2{{implicitly initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial4 = { .iter = arr }; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + struct S s_implicit_partial5 = { .end = arr }; + // expected-warning@+2{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial6 = { .end = arr, .start = arr }; + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial7 = { .iter = arr, .start = arr }; + // expected-warning@+1{{implicitly initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial8 = { .iter = arr, .end = arr }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial1 = { arr, 0, 0 }; + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial2 = { arr, arr+1, 0 }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial3 = { .start = arr, .iter = 0, .end = 0 }; + // expected-warning@+2{{initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial4 = { .iter = arr, .end = 0, .start = 0 }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + struct S s_partial5 = { .end = arr, .start = 0, .iter = 0 }; + // expected-warning@+2{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial6 = { .end = arr, .start = arr, .iter = 0 }; + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial7 = { .iter = arr, .start = arr, .end = 0 }; + // expected-warning@+1{{initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial8 = { .iter = arr, .end = arr, .start = 0 }; + struct S s_designate_all = { .iter = &arr[1], .end = arr + 2, .start = arr }; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c b/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c new file mode 100644 index 0000000000000..4b902ad07cbdb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify=with-checks -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update %s +// RUN: %clang_cc1 -fsyntax-only -verify=without-checks -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s +#include + +// without-checks-no-diagnostics + +void foo(int *__ended_by(end) start, int * end) { + // with-checks-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} + *start++ = 0; +} + +void bar(int *__ended_by(end) start, int * end) { + // with-checks-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} + *(start = start+1) = 0; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_assignments.c b/clang/test/BoundsSafety/Sema/ended_by_assignments.c new file mode 100644 index 0000000000000..77f9bae76146b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_assignments.c @@ -0,0 +1,57 @@ +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +#include + +int *__ended_by(end) func_ret_end(int *end) { + // rs-error@+1{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'func_ret_end' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + end = 0; + return end; +} + +int *__ended_by(end) func_ret_end2(int *__ended_by(end) start, int *end) { + return start + 1; +} + +void func_out_start_out_end(int *__ended_by(*out_end) *out_start, + int **out_end) { + out_start = 0; + out_end = 0; +} + +void func_out_start_in_end(int *__ended_by(end) *out_start, + int *end) { + *out_start = *out_start + 1; + // expected-error@+1{{parameter 'end' referred to by an indirect '__ended_by' pointer is implicitly read-only}} + end--; +} + +void func_out_start_in_end2(int *__ended_by(end) *out_start, + int *end) { + // expected-error@+1{{parameter 'end' referred to by an indirect '__ended_by' pointer is implicitly read-only}} + end--; +} + +void func_out_start_in_end3(int *__ended_by(end) *out_start, + int *end) { + *out_start = *out_start + 1; +} + +void func_in_start_out_end(int *__ended_by(*out_end) start, + int **out_end) { + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect end pointer is implicitly read-only}} + start++; +} + +void func_in_start_out_end2(int *__ended_by(*out_end) start, + int **out_end) { + *out_end = *out_end - 1; + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect end pointer is implicitly read-only}} + start++; +} + +void func_in_start_out_end3(int *__ended_by(*out_end) start, + int **out_end) { + *out_end = *out_end - 1; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_decls.c b/clang/test/BoundsSafety/Sema/ended_by_decls.c new file mode 100644 index 0000000000000..503bb340c724c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_decls.c @@ -0,0 +1,107 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +extern int *end; +extern int *__ended_by(end) start; + +int *end2; +extern int *__ended_by(end2) start2; +// expected-error@-1{{pointer with '__ended_by' and the argument of the attribute must be defined in the same translation unit}} + +extern int *end3; +int *__ended_by(end3) start3; +// expected-error@-1{{pointer with '__ended_by' and the argument of the attribute must be defined in the same translation unit}} + +extern int *end4; +extern int *__ended_by(end4) start4; + +void *end_share; +void *__ended_by(end_share) start5; +// expected-note@-1{{previous use is here}} +void *__ended_by(end_share) start6; +// expected-error@-1{{variable 'end_share' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + +int *const const_end = 0; +int *data_const_end __unsafe_late_const; + +int *__ended_by(const_end) start_with_const_end; +void test_global_start_with_const_end() { + start_with_const_end = 0; +} + +int *__ended_by(data_const_end) start_with_data_const_end; +void test_global_start_with_data_const_end() { + start_with_data_const_end = 0; +} + +void test_extern_start_end() { + start = 0; + end = 0; +} + +void test_const_end(int *__ended_by(const_end) start); +void test_data_const_end(int *__ended_by(data_const_end) start); + +void test_redecl(int *__ended_by(end) start, int* end); +void test_redecl(int *__ended_by(end) start, int* end) {} + +void *__ended_by(end) test_redecl_return(int *__ended_by(end) start, int* end); +void *__ended_by(end) test_redecl_return(int *__ended_by(end) start, int* end); + + +// FIXME: weird error messages +// expected-error@+3{{pointer arithmetic on single pointer 'end' is out of bounds; consider adding '__counted_by' to 'end'}} +// expected-note@+2{{pointer 'end' declared here}} +// expected-error@+1{{'__ended_by' attribute requires a pointer type argument}} +void foo_bin_end(int *__ended_by(end+1) start, int* end); +void foo_cptr_end(int *__ended_by(end) start, char* end); +// expected-error@+1{{'__ended_by' attribute requires a pointer type argument}} +void foo_int_end(int *__ended_by(i) start, int i); +void foo_out_start_out_end(int *__ended_by(*out_end) *out_start, int **out_end); +void foo_out_end(int *__ended_by(*out_end) start, int **out_end); +int *__ended_by(end) foo_ret_end(int *end); + +// expected-note@+3{{add a count attribute}} +// expected-error@+2{{parameter of array type 'int[]' decays to a __single pointer}} +// expected-error@+1{{'__ended_by' attribute only applies to pointer arguments}} +void foo_end_in_bracket(int buf[__ended_by(end)], int *end); + +// expected-error@+1{{invalid argument expression to bounds attribute}} +int *__ended_by((int *)42) invalid_arg_in_ret_proto(void); + +// Check function with no prototype. DO NOT put 'void' in the parentheses. +// expected-error@+1{{invalid argument expression to bounds attribute}} +int *__ended_by((int *)42) invalid_arg_in_ret_noproto(); + +struct S { + int *end; + int *__ended_by(end) start; + int *__ended_by(end-1) start_1; // expected-error{{invalid argument expression to bounds attribute}} + int *__bidi_indexable bidi_end; + int *__ended_by(bidi_end) start_with_bidi_end; // expected-error{{end-pointer must be '__single'}} + int *__ended_by(end) __bidi_indexable start2; // expected-error{{pointer cannot be '__ended_by' and '__bidi_indexable' at the same time}} + int *__indexable __ended_by(end) start3; // expected-error{{pointer cannot be '__ended_by' and '__indexable' at the same time}} +}; + +struct T { + void *p1; + void *__ended_by(p1) p2; + void *__ended_by(p2 - 1) p3; // expected-error{{invalid argument expression to bounds attribute}} +}; + +struct U { + void *__sized_by(8) p1; + void *__ended_by(p1 - 1) p2; // expected-error{{invalid argument expression to bounds attribute}} +}; + +int baz(void) { + int *end; + int *__ended_by(end) start; + return 0; +} + +void type_of(int * __ended_by(end) p, int * end) { + __typeof__(p) p2; // expected-error{{__typeof__ on an expression of type 'int *__single __ended_by(end)' (aka 'int *__single') is not yet supported}} +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_incdec.c b/clang/test/BoundsSafety/Sema/ended_by_incdec.c new file mode 100644 index 0000000000000..e6204e7b8bce5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_incdec.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__ended_by(end) start, int *end) { + start--; // expected-error{{negative pointer arithmetic on pointer that starts the '__ended_by' chain always traps}} + ++end; // expected-error{{positive pointer arithmetic on end pointer always traps}} +} + +void Bar(int *__ended_by(end) start, int *end) { + start+=4; // expected-error{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} +} + +typedef struct { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +} T; + +void Baz(T *tp) { + --tp->start; // expected-error{{negative pointer arithmetic on pointer that starts the '__ended_by' chain always traps}} + tp->end++; // expected-error{{positive pointer arithmetic on end pointer always traps}} +} + +void Qux(T *tp) { + tp->start = tp->start; + tp->end = tp->end; + tp->iter--; +} + +void Quux(T *tp) { + tp->start++; + ++tp->iter; + --tp->end; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_locals.c b/clang/test/BoundsSafety/Sema/ended_by_locals.c new file mode 100644 index 0000000000000..9349f44bab503 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_locals.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// expected-no-diagnostics + +void init_both(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +void init_end(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr; // rdar://103382748 __ended_by needs to be initialized pairwise with end pointer +} + +void init_start(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr; // rdar://103382748 while not breaking bounds safety, this will always trap unless `asdf` is null + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +void init_neither(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr; + const int * __ended_by(myEndPtr) myEndedByPtr; + myEndPtr = asdf + asdf_len; + myEndedByPtr = asdf; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c b/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c new file mode 100644 index 0000000000000..42856d5478de5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +struct CountedData { + int *__counted_by(len) buf; + int len; +}; + +struct EndedData { + int *__ended_by(end) start; + int *end; +}; + +void Foo(struct CountedData *cp, struct EndedData *ep) { + cp->buf = ep->start; + cp->len = 10; +} + +int glen; +void Bar(struct CountedData *cp, struct EndedData *ep) { + cp->buf = ep->end; + cp->len = glen; +} diff --git a/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c b/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c new file mode 100644 index 0000000000000..2309a23386052 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -verify %s + + +#include + +struct saddr { + unsigned len; + char fam[__counted_by(len)]; +}; + +void test(struct saddr *sa, unsigned length) { + // expected-error@+1{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + sa = sa + 1; + sa->len = length; +} diff --git a/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c b/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c new file mode 100644 index 0000000000000..1363d9a56b983 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +// expected-note@extern-array-mock.h:3 {{'externArray' declared here}} +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +extern unsigned externArray[__counted_by(10)]; + +extern const unsigned externCountedArray[__counted_by(2)]; +const unsigned externCountedArray[] = {1, 2}; + +extern const unsigned externCountVar; +extern const unsigned externCountedArrayVar[__counted_by(externCountVar)]; +const unsigned externCountVar = 2; +const unsigned externCountedArrayVar[] = {1, 2}; + +extern const unsigned externCountVar2; +extern const unsigned externCountedArrayVar2[__counted_by(externCountVar2)]; +const unsigned externCountedArrayVar2[] = {1, 2}; +const unsigned externCountVar2 = 2; + +extern const unsigned externCountVar3; +extern const unsigned externCountedArrayVar3[__counted_by(externCountVar3)]; +const unsigned externCountedArrayVar3[] = {1, 2}; +const unsigned externCountVar3 = 3; // rdar://129246717 this should be an error + +extern const unsigned externCountVar4; +extern const unsigned externCountedArrayVar4[__counted_by(externCountVar4)]; +const unsigned externCountVar4 = 3; // rdar://129246717 this should be an error +const unsigned externCountedArrayVar4[] = {1, 2}; + +extern const unsigned externCountVar5; +extern const unsigned externCountedArrayVar5[__counted_by(externCountVar5)]; +const unsigned externCountedArrayVar5[] = {1, 2}; // rdar://129246717 this should be an error + +extern const unsigned externCountVar6; +extern const unsigned externCountedArrayVar6[__counted_by(externCountVar6)]; +const unsigned externCountVar6 = 3; // rdar://129246717 this should be an error + +extern const unsigned externCountedArrayUnsafe[__counted_by(10)]; +const unsigned externCountedArrayUnsafe[] = {1, 2}; // rdar://129246717 this should be an error + +void bar(const unsigned *pointer); + +void foo(void){ + f(); + bar(externArray); + unsigned *ptr = externArray; +} diff --git a/clang/test/BoundsSafety/Sema/extern-incomplete-array.c b/clang/test/BoundsSafety/Sema/extern-incomplete-array.c new file mode 100644 index 0000000000000..8e389bcaf6f1d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/extern-incomplete-array.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +void bar(const unsigned *pointer); + +void foo(void){ + f(); + bar(externArray); // expected-warning{{accessing elements of an unannotated incomplete array always fails at runtime}} + unsigned *ptr = externArray; // expected-warning{{accessing elements of an unannotated incomplete array always fails at runtime}} +} + +extern const char baz[__null_terminated]; + +void qux(void) { + const char *__null_terminated x = baz; // ok +} diff --git a/clang/test/BoundsSafety/Sema/false-flexbase-array.c b/clang/test/BoundsSafety/Sema/false-flexbase-array.c new file mode 100644 index 0000000000000..6254a3ace1034 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/false-flexbase-array.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Regression test for rdar://133766202 +// expected-no-diagnostics + +struct S { + int field1; + int field2; +}; + +void foo() { + struct S arr[2]; + arr->field1 = 3; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c new file mode 100644 index 0000000000000..e17e06e727bda --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void init_null(void) { + struct flexible *__single s = 0; +} + +void init_null_bidi(void) { + struct flexible *s = 0; +} + +void init_casted_null(void) { + struct flexible *__single s = (struct flexible *)0; +} + +void init_casted_null_bidi(void) { + struct flexible *s = (struct flexible *)0; +} + +void assign_null(void) { + struct flexible *__single s; + s = 0; +} + +void assign_casted_null(void) { + struct flexible *__single s; + s = (struct flexible *)0; +} + +void impl_init_null_member_ref(void) { + struct flexible *__single s; + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void init_null_member_ref(void) { + struct flexible *__single s = 0; + s->count = 10; // expected-error{{base of member reference is a null pointer}} +} + +void init_casted_null_member_ref(void) { + struct flexible *__single s = (struct flexible *)0; + s->count = 0; // expected-error{{base of member reference is a null pointer}} +} + +void assign_null_member_ref(void) { + struct flexible *__single s; + s = 0; + s->count = 2; // expected-error{{base of member reference is a null pointer}} +} + +void assign_casted_null_member_ref(void) { + struct flexible *__single s; + s = (struct flexible *)0; + s->count = 7; // expected-error{{base of member reference is a null pointer}} +} + +void assign_casted_null_member_ref_bidi(void) { + struct flexible *s; + s = (struct flexible *)0; + s->count = 7; // XXX: currently, flexible array member operations are analyzed only when their base is a single pointer type +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c new file mode 100644 index 0000000000000..58aca5b09a629 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void pass_to_bidi_indexable(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__bidi_indexable' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *b = flex; +} + +void pass_to_single(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__single' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *__single s = flex; +} + +void pass_to_single_forge(struct flexible *__unsafe_indexable flex) { + struct flexible *__single s = __unsafe_forge_single(struct flexible *, flex); +} + +void pass_to_single2(struct flexible *__unsafe_indexable flex) { + struct flexible *__single s; + // expected-error@+1{{assigning to 'struct flexible *__single' from incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + s = flex; +} + +void pass_to_single_assign(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__single' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *__single s = flex; + s->count = flex->count; +} + +struct flex_unsafe { + int count; + int elems[]; +}; + +void promote_unsafe_to_bidi_indexable(struct flex_unsafe *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flex_unsafe *__bidi_indexable' with an expression of incompatible type 'struct flex_unsafe *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flex_unsafe *b = flex; +} + +void promote_unsafe_to_single(struct flex_unsafe *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flex_unsafe *__bidi_indexable' with an expression of incompatible type 'struct flex_unsafe *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flex_unsafe *s = flex; +} + +void promote_unsafe_to_unsafe(struct flex_unsafe *__unsafe_indexable flex) { + struct flex_unsafe *__unsafe_indexable s = flex; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..69b447aa42efc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void init_single(void *p) { + struct flexible *__single s = p; +} + +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} + +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} + +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} + +void init_single_member_ref(void *p) { + struct flexible *__single s = p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void init_casted_single_member_ref(void *p) { + struct flexible *__single s = (struct flexible *)p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_single_member_ref(void *p) { + struct flexible *__single s; + s = p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 2; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_casted_single_member_ref(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 7; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_casted_single_member_ref_bidi(void *p) { + struct flexible *s; + s = (struct flexible *)p; + s->count = 7; // XXX: currently, flexible array member operations are analyzed only when their base is a single pointer type +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c b/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c new file mode 100644 index 0000000000000..0c00973bed026 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s + +// expected-no-diagnostics + +#include +#include + +struct flex { + uint8_t count; + uint8_t body[__counted_by(count - 1)]; +}; + +void pass_ptr(const void *__sized_by(size), unsigned size); + +void foo(struct flex *f) { + pass_ptr(f, 8); +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c b/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c new file mode 100644 index 0000000000000..6f0b6da99be38 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fbounds-safety -verify %s -o /dev/null +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -o /dev/null + +#include + +typedef struct { + int len; + unsigned fam[__counted_by(len)]; +} S; + +void bar(const unsigned *pointer); + +void foo(void){ + S s; + bar(s.fam); + unsigned *ptr = s.fam; + ptr = __unsafe_forge_bidi_indexable(unsigned *, s.fam, s.len * sizeof(unsigned)); +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c b/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c new file mode 100644 index 0000000000000..1f803b29b0dbc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flex { + int len; + char fam[]; +}; + +struct flex_with_count { + int len; + char fam[__counted_by(len)]; +}; + +struct flex_in_union { + struct flex fnc; // expected-warning{{field 'fnc' with variable sized type 'struct flex' not at the end of a struct or class is a GNU extension}} + union _u{ + struct flex_with_count fc; + int i; + } u; // expected-warning{{field 'u' with variable sized type 'union _u' not at the end of a struct or class is a GNU extension}} + + int dummy; +}; + +char foo(struct flex_in_union * bar) { + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + return bar->dummy ? bar->fnc.fam[bar->fnc.len - 1] : bar->u.fc.fam[bar->u.fc.len - 1]; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..1e1a56720dddd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c @@ -0,0 +1,145 @@ +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +void good(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + s->offs = 5; + s->len = 10; +} + +void missing_offs(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->len' requires corresponding assignment to 's->offs'; add self assignment 's->offs = s->offs' if the value has not changed}} + s->len = 10; +} + +void missing_s(S *s) { + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s->len = 10; +} + +void missing_len_offs(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; +} + +void missing_len(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->offs' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->offs = 5; +} + +// rdar://132802568 +void flexbase_middle(S *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s = (S *)&arr[5]; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + s->len = 10; +} + +void flexbase_last(S *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s->len = 10; + s = (S *)&arr[5]; +} + +typedef struct { + int len; + int off1; + int off2; + int fam[__counted_by(len - (off1 + off2))]; +} S2; + +void good2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + s->len = 10; + s->off1 = 2; + s->off2 = 3; +} + +void missing_off1_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->len' requires corresponding assignment to 's->off1'; add self assignment 's->off1 = s->off1' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->len' requires corresponding assignment to 's->off2'; add self assignment 's->off2 = s->off2' if the value has not changed}} + s->len = 10; +} + +void missing_s2(S2 *s) { + // expected-error@+1{{assignment to 's->off1' requires an immediately preceding assignment to 's' with a wide pointer}} + s->off1 = 3; + s->off2 = 2; + s->len = 10; +} + +void missing_len_off1_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; +} + + +void missing_len_off1(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->off2' requires corresponding assignment to 's->off1'; add self assignment 's->off1 = s->off1' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->off2' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off2 = 1; +} + +void missing_len_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->off1' requires corresponding assignment to 's->off2'; add self assignment 's->off2 = s->off2' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->off1' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off1 = 3; +} + +void missing_len2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->off1' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off1 = 2; + s->off2 = 3; +} + +void missing_len_off1_2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; +} + +// rdar://132802568 +void flexbase_middle2(S2 *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->len = 10; + s->off1 = 2; + s = (S2 *)&arr[5]; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + s->off2 = 3; +} + +void flexbase_last2(S2 *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->len = 10; + s->off1 = 2; + s->off2 = 3; + s = (S2 *)&arr[5]; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c b/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c new file mode 100644 index 0000000000000..2f34c0d7ae040 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c @@ -0,0 +1,126 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct flexible { + int count; + int elems[__counted_by(count)]; // \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} +} flex_t; + +// just to make sure this is OK +flex_t zeroes = {}; +flex_t zeroes2 = { 0 }; +flex_t zeroes3 = { .count = 0 }; +struct flexible flex = { 3, {1, 2, 3} }; +struct flexible flex2 = { .count = 3, .elems = { 1, 2, 3} }; +struct flexible flex3 = {3, { [2] = 3 } }; +struct flexible flex4 = { .count = 3, .elems = { [2] = 3} }; +struct flexible *returning_flexible_ptr(void); +flex_t *returning_flex_ptr(void); +void accepting_flexible_ptr(struct flexible *p); +void accepting_flex_ptr(flex_t *p); + +// these shouldn't work +struct flexible returning_flexible(void); // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +flex_t returning_flex(void); // expected-error{{-fbounds-safety forbids passing 'flex_t' (aka 'struct flexible') by copy}} +void accepting_flexible(struct flexible p); // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +void accepting_flex(flex_t p); // expected-error{{-fbounds-safety forbids passing 'flex_t' (aka 'struct flexible') by copy}} + +flex_t negative_count_die = { .count = -1, {1} }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to -1}} +flex_t negative_count_die2 = { .count = -1 }; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} +flex_t negative_count = { -1, {1} }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to -1}} +flex_t zero_count = { 0, {1, 2, 3 } }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 0}} +flex_t count_too_small = { 2, {1, 2, 3} }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 2}} +flex_t count_too_large = {4, {1, 2, 3} }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 4}} +flex_t count_too_large_2 = {3}; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 3}} +flex_t count_too_large_die = { .count = 3 }; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 3}} +flex_t count_too_large_die2 = { .count = 3, .elems = { 0 } }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 3}} +flex_t count_too_large_die3 = { .count = 3, .elems = { [1] = 0 } }; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 3}} + +void foo(void) { + flex_t flex_local = flex; // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} + flex_local = flex; // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +} + +flex_t *bar(flex_t *__bidi_indexable flex) { + ++flex; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + flex++; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + --flex; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + flex--; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + (void) (flex + 1); // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + (void) (flex - 1); // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} +} + +void baz(flex_t *__counted_by(1) flex); // expected-error{{cannot apply '__counted_by' attribute to 'flex_t *' (aka 'struct flexible *') because 'flex_t' (aka 'struct flexible') has unknown size; did you mean to use '__sized_by' instead?}} +void qux(flex_t *__sized_by(siz) flex, unsigned siz); + +void quux(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; // OK. run-time check inserted with flex->count. +} + +void quuz(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; // OK. + flex->count = 10; +} + +void corge(flex_t *flex, char *__bidi_indexable buf) { + flex->count = 10; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} + flex = (flex_t *)buf; +} + +void grault(flex_t *flex) { + flex->count = 10; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} +} + +void garply(char *__bidi_indexable buf) { + flex_t *__single flex = (flex_t *)buf; + flex->count = 10; +} + +void waldo(char *__bidi_indexable buf) { + flex_t *flex; + flex->count = 10; // OK. +} + +void fred(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; + int a = 5; + flex->count = a; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} +} + +flex_t g_flex = {4, {1, 2, 3, 4}}; +void global_flex_count_assign(unsigned new_count) { + g_flex.count = new_count; // run-time check +} +void global_flex_count_increment() { + g_flex.count++; // expected-error{{incrementing 'g_flex.count' always traps}} +} + +void global_flex_count_decrement() { + g_flex.count--; // ok. run-time check +} + +void global_flex_count_compound_assign(unsigned diff) { + g_flex.count += diff; // run-time check +} + +typedef struct flexible_unannotated { + int count; + int elems[]; // \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} +} flex_bad_t; + +flex_bad_t bad1 = { 0 }; // expected-error{{flexible array member is initialized without a count}} +flex_bad_t bad2 = {1, {1} }; // expected-error{{flexible array member is initialized without a count}} diff --git a/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c b/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c new file mode 100644 index 0000000000000..c6dcac0e222c9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int f; + int vla[]; +}; + +void Test() { + struct Foo f; + (void) __unsafe_forge_bidi_indexable(int *, f.vla, 0); + (void) __unsafe_forge_single(int *, f.vla); + (void) __unsafe_forge_terminated_by(int *, f.vla, 255); +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/forge-ptr-expr.c b/clang/test/BoundsSafety/Sema/forge-ptr-expr.c new file mode 100644 index 0000000000000..2d51e1963f214 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/forge-ptr-expr.c @@ -0,0 +1,152 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,bs %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,bs %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify=expected,bsa %s + +#include + +/* Define to nothing in attribute-only mode */ +#ifndef __indexable +#define __indexable +#endif + +#define forge_bidi_indexable(A, B) __unsafe_forge_bidi_indexable(int *, A, B) +#define forge_single(A) __unsafe_forge_single(int *, A) +#define forge_terminated_by(A, B) __unsafe_forge_terminated_by(int *, A, B) + +void Test1() { + int *__indexable ptrArray = forge_bidi_indexable(0, sizeof(int)); + int *__single ptr = forge_single(0); + int *__terminated_by(0) ptr2 = forge_terminated_by(0, 0); +} + +#define MACRO_ZERO 0 +#define MACRO_ONE 1 +#define MACRO_MINUS_ONE -1 + +/* In C++ enum is not an int, use macros to make the test pass */ +#ifdef __cplusplus +#define ZERO 0 +#define ONE 1 +#else +enum Enum { ZERO, ONE}; +#endif + +int global_int_var; +int* global_int_ptr_var; +struct StructFoo { + int member_int_var; + int* member_int_ptr_var; +}; +int get_negative_int(void) { return -1; } +#define NULL 0 + +#define MACRO_UINT64_MAX (~0ull) +#define MACRO_UINT32_MAX (~0u) + +void Test2() { + // bs-error@+2{{'__unsafe_forge_bidi_indexable' requires a pointer, array or integer address argument}} + // bsa-error-re@+1{{{{.*}}'double'{{.*}}pointer type}} + (void) forge_bidi_indexable(0., 0); + (void) forge_single(0.); // expected-error{{'__unsafe_forge_single' requires a pointer, array or integer address argument}} + (void) forge_terminated_by(0., 0); // expected-error{{'__unsafe_forge_terminated_by' requires a pointer, array or integer address argument}} + + (void) forge_bidi_indexable(ZERO, 0); + (void) forge_bidi_indexable(ONE, 0); + (void) forge_bidi_indexable(MACRO_ZERO, 0); + (void) forge_bidi_indexable(MACRO_ONE, 0); + (void) forge_bidi_indexable(NULL, 0); + + (void) forge_single(ZERO); + (void) forge_single(ONE); + (void) forge_single(MACRO_ZERO); + (void) forge_single(MACRO_ONE); + (void) forge_single(NULL); + + (void) forge_terminated_by(ZERO, 0); + (void) forge_terminated_by(ONE, 0); + (void) forge_terminated_by(MACRO_ZERO, 0); + (void) forge_terminated_by(MACRO_ONE, 0); + (void) forge_terminated_by(NULL, 0); + + struct StructFoo F; + // bs-error@+2{{'__unsafe_forge_bidi_indexable' requires a pointer, array or integer address argument}} + // bsa-error-re@+1{{{{.*}}'struct StructFoo'{{.*}}pointer type}} + (void) forge_bidi_indexable(F, 0); + (void) forge_bidi_indexable(&F, 0); + (void) forge_bidi_indexable(&F.member_int_var, 0); + (void) forge_bidi_indexable(F.member_int_ptr_var, 0); + + (void) forge_single(F); // expected-error{{'__unsafe_forge_single' requires a pointer, array or integer address argument}} + (void) forge_single(&F); + (void) forge_single(&F.member_int_var); + (void) forge_single(F.member_int_ptr_var); + + (void) forge_terminated_by(F, 0); // expected-error{{'__unsafe_forge_terminated_by' requires a pointer, array or integer address argument}} + (void) forge_terminated_by(&F, 0); + (void) forge_terminated_by(&F.member_int_var, 0); + (void) forge_terminated_by(F.member_int_ptr_var, 0); + + int local_int_var = 42; + int* local_int_ptr_var; + (void) forge_bidi_indexable(&local_int_var, 0); + (void) forge_bidi_indexable(local_int_ptr_var, 0); + (void) forge_bidi_indexable(&global_int_var, 0); + (void) forge_bidi_indexable(global_int_ptr_var, 0); + (void) forge_bidi_indexable(~0ull, 0); + (void) forge_bidi_indexable(-1, 0); // bs-error{{negative address argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(MACRO_MINUS_ONE, 0); // bs-error{{negative address argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(&local_int_var, MACRO_UINT64_MAX); + // bs-error@-1{{negative size argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(0, MACRO_UINT32_MAX); + (void) forge_bidi_indexable(0, (unsigned char) 240); + + (void) forge_single(&local_int_var); + (void) forge_single(local_int_ptr_var); + (void) forge_single(&global_int_var); + (void) forge_single(global_int_ptr_var); + (void) forge_single(~0ull); + (void) forge_single(-1); // expected-error{{negative address argument to '__unsafe_forge_single'}} + (void) forge_single(MACRO_MINUS_ONE); // expected-error{{negative address argument to '__unsafe_forge_single'}} + + (void) forge_terminated_by(&local_int_var, 0); + (void) forge_terminated_by(local_int_ptr_var, 0); + (void) forge_terminated_by(&global_int_var, 0); + (void) forge_terminated_by(global_int_ptr_var, 0); + (void) forge_terminated_by(~0ull, 0); + (void) forge_terminated_by(-1, 0); // expected-error{{negative address argument to '__unsafe_forge_terminated_by'}} + (void) forge_terminated_by(MACRO_MINUS_ONE, 0); // expected-error{{negative address argument to '__unsafe_forge_terminated_by'}} +} + +void Test3() { + float f; + (void) forge_bidi_indexable(0, f); // bs-error{{'__unsafe_forge_bidi_indexable' requires an integer size argument}} + struct StructFoo s; + (void) forge_bidi_indexable(0, s); // bs-error{{'__unsafe_forge_bidi_indexable' requires an integer size argument}} + (void) forge_bidi_indexable(0, -4); // bs-error{{negative size argument to '__unsafe_forge_bidi_indexable'}} + + (void) forge_terminated_by(0, f); // expected-error{{'__terminated_by__' attribute requires an integer constant}} + (void) forge_terminated_by(0, s); // expected-error{{'__terminated_by__' attribute requires an integer constant}} +} + +int global_array[6]; +void Test4() { + int local_array[6]; + (void) forge_bidi_indexable(local_array, 0); + (void) forge_single(local_array); + (void) forge_terminated_by(local_array, 0); + + (void) forge_bidi_indexable(global_array, 0); + (void) forge_single(global_array); + (void) forge_terminated_by(global_array, 0); +} + +void Test5() { + // Function pointers are supported. + (void) forge_bidi_indexable(Test5, 0); + (void) forge_single(Test5); + (void) forge_terminated_by(Test5, 0); +} diff --git a/clang/test/BoundsSafety/Sema/fptr-count-return.c b/clang/test/BoundsSafety/Sema/fptr-count-return.c new file mode 100644 index 0000000000000..85f05a0673b91 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/fptr-count-return.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump -verify %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -verify %s | FileCheck %s +#include + +// CHECK: VarDecl {{.*}} fptr 'int *__single*__single __counted_by(len)(*__single)(int)' +int **__counted_by(len) (*fptr)(int len); + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} +void *__counted_by(len) (*fptr1)(int len); + +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int *__counted_by(len)* (*fptr2)(int len); + +// CHECK: VarDecl {{.*}} fptr3 'int *__single __sized_by(len1)(*__single)(int)' +// CHECK: VarDecl {{.*}} fptr4 'int *__single __sized_by(len2)(*__single)(void *__single __sized_by(len2), int)' +int *__sized_by(len1)(*fptr3)(int len1), *__sized_by(len2)(*fptr4)(void *__sized_by(len2), int len2); + +// CHECK: VarDecl {{.*}} fptr5 'void *__single __sized_by(len1)(*__single)(int)' +void *__sized_by(len1) (*fptr5)(int len1), + +// expected-error@+2{{use of undeclared identifier 'len1'; did you mean 'len2'?}} +// expected-note@+1{{'len2' declared here}} + *__sized_by(len1) (*fptr6)(void *__sized_by(len2), unsigned len2); + +int glen; +// CHECK: VarDecl {{.*}} fptr7 'void *__single __sized_by(glen)(*__single)(int)' +void *__sized_by(glen) (*fptr7)(int glen); // ok. glen shadowed. + +// expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} +void *__sized_by(glen) (*fptr8)(); + + +// CHECK: RecordDecl {{.*}} struct T1 definition +// CHECK: |-FieldDecl {{.*}} len 'int' +// CHECK: `-FieldDecl {{.*}} fptr 'void *__single __sized_by(len)(*__single)(int)' +struct T1 { + int len; + void *__sized_by(len) (*fptr)(int len); +}; + +struct T2 { + int field_len; + // expected-error@+1{{use of undeclared identifier 'field_len'}} + int *__sized_by(field_len) (*fptr)(int len); +}; + +struct T3 { + int field_len; + // expected-error@+1{{use of undeclared identifier 'field_len'}} + void *__sized_by(field_len) (*fptr)(); +}; + +// CHECK: RecordDecl {{.*}} struct T4 definition +// CHECK: |-FieldDecl {{.*}} fptr1 'void *__single __sized_by(len1)(*__single)(unsigned int)' +// CHECK: |-FieldDecl {{.*}} fptr2 'void *__single __sized_by(len2)(*__single)(unsigned int)' +// CHECK: |-FieldDecl {{.*}} fptr3 'void *__single __sized_by(len3)(*__single)(unsigned int)' +struct T4 { + void *__sized_by(len1) (*fptr1)(unsigned len1), + *__sized_by(len2) (*fptr2)(unsigned len2); + + void *__sized_by(len3) (*fptr3)(unsigned len3), + // expected-error@+2{{use of undeclared identifier 'len3'; did you mean 'len4'?}} + // expected-note@+1{{'len4' declared here}} + *__sized_by(len3) (*fptr4)(unsigned len4); +}; diff --git a/clang/test/BoundsSafety/Sema/fptr-not-indexable.c b/clang/test/BoundsSafety/Sema/fptr-not-indexable.c new file mode 100644 index 0000000000000..8d278f497371a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/fptr-not-indexable.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Test(void) { + void (*fptrImplicitSingleOk)(void) = &Test; + void (*__single fptrExplicitSingleOk)(void) = &Test; + void (*__unsafe_indexable fptrExplicitUnsafeIndexableOk)(void) = &Test; + void (*__indexable fptrExplicitIndexableError)(void) = &Test; // expected-error{{function pointers cannot be indexable}} + void (*__bidi_indexable fptrExplicitBidiIndexableError)(void) = &Test; // expected-error{{function pointers cannot be indexable}} + typedef void (*fun_ptr)(void); + fun_ptr fptrImplicitSingleTypeDefOk = &Test; + typedef void (*__single fun_ptr_single)(void); + + fun_ptr_single fptrExplicitSingleTypeDefOk1 = &Test; + fun_ptr __single fptrExplicitSingleTypeDefOk2 = &Test; + + typedef void (*__unsafe_indexable fun_ptr_unsafe)(void); + fun_ptr_unsafe fptrExplicitUnsafeIndexableTypeDefOk1 = &Test; + fun_ptr __unsafe_indexable fptrExplicitUnsafeIndexableTypeDefOk2 = &Test; + + typedef void (*__indexable fun_ptr_idxble)(void); // expected-error{{function pointers cannot be indexable}} + fun_ptr_idxble fptrExplicitIndexableTypeDefError1 = &Test; + fun_ptr __indexable ptrExplicitIndexableTypeDefError2 = &Test; // expected-error{{function pointers cannot be indexable}} + + typedef void (*__bidi_indexable fun_ptr_bidi_idxble)(void); // expected-error{{function pointers cannot be indexable}} + fun_ptr_bidi_idxble fptrExplicitBidiIndexableTypeDefError1 = &Test; + fun_ptr __bidi_indexable fptrExplicitBidiIndexableTypeDefError2 = &Test; // expected-error{{function pointers cannot be indexable}} +} diff --git a/clang/test/BoundsSafety/Sema/global-count-pointer.c b/clang/test/BoundsSafety/Sema/global-count-pointer.c new file mode 100644 index 0000000000000..b0326248f5d94 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-count-pointer.c @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// TODO: rdar://85557264 +int len = 0; +int *__counted_by(len) ptr; +void *__sized_by(len) ptr_share_len; + +const unsigned const_len = 0; +int *__counted_by(const_len) ptr_with_const_len; + +int late_const_len __unsafe_late_const = 0; +void *__sized_by(late_const_len) ptr_with_late_const_len; + +int arrlen; +// expected-warning@+1{{array with '__counted_by' and the argument of the attribute should be defined in the same translation unit}} +extern int arr[__counted_by(arrlen)]; + +extern int arrlen2; +extern int arr2[__counted_by(arrlen2)]; + +extern unsigned extlen; +extern void *__sized_by(extlen) extptr; + +extern unsigned extlen2; +// expected-error@+1{{pointer with '__counted_by' and the argument of the attribute must be defined in the same translation unit}} +int *__counted_by(extlen2) ptr2; +// expected-error@+1{{pointer with '__sized_by' and the argument of the attribute must be defined in the same translation unit}} +void *__sized_by(extlen2) ptr3; +// expected-error@+1{{pointer with '__counted_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +int *__counted_by_or_null(extlen2) ptr4; +// expected-error@+1{{pointer with '__sized_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +void *__sized_by_or_null(extlen2) ptr5; + +unsigned long len2; +// expected-error@+1{{pointer with '__counted_by' and the argument of the attribute must be defined in the same translation unit}} +extern int *__counted_by(len2) extptr2; +// expected-error@+1{{pointer with '__sized_by' and the argument of the attribute must be defined in the same translation unit}} +extern void *__sized_by(len2) extptr3; +// expected-error@+1{{pointer with '__counted_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +extern int *__counted_by_or_null(len2) extptr4; +// expected-error@+1{{pointer with '__sized_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +extern void *__sized_by_or_null(len2) extptr5; + +extern int redecl_len1; +extern void *__sized_by(redecl_len1) redecl_ptr1; +int redecl_len1; +void *__sized_by(redecl_len1) redecl_ptr1; + +extern int redecl_len2; +extern int *__sized_by(redecl_len2) redecl_ptr2; // expected-note{{'redecl_ptr2' declared here}} +int redecl_len2; +int *__counted_by(redecl_len2) redecl_ptr2; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} + +extern int redecl_len3; +extern int *redecl_ptr3; // expected-note 4{{'redecl_ptr3' declared here}} +int redecl_len3; +int *__counted_by(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int *__sized_by(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int *__counted_by_or_null(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int *__sized_by_or_null(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} + +extern int redecl_len4; +extern int *__counted_by(redecl_len4) redecl_ptr4; +extern int redecl_len5; +extern int *__counted_by(redecl_len5) redecl_ptr5; // expected-note 4{{'redecl_ptr5' declared here}} +int redecl_len4; +int *__counted_by(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int *__sized_by(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int *__counted_by_or_null(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int *__sized_by_or_null(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} + +int len_2 = 2; +int *__counted_by(len_2 - 2) ptr_with_expr_count; diff --git a/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c b/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c new file mode 100644 index 0000000000000..146d193c54501 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c @@ -0,0 +1,36 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// expected-no-diagnostics + +// This file exercises buildAndChainOldCountCheck for nested MemberExprs + +struct nested_fam { + int dummy; + int len; +}; +struct outer { + struct nested_fam hdr2; + int dummy; +}; +struct outerouter { + struct outer hdr; + char fam[__counted_by(hdr.hdr2.len)]; +}; + +struct outerouter s = { + .hdr.hdr2.len = 2, + .fam = {99,98} +}; + +void foo() { + s.hdr.hdr2.len = 1; +} + +void bar() { + // rdar://127523062 error here + struct outerouter t; + t.hdr.hdr2.len = 8; +} diff --git a/clang/test/BoundsSafety/Sema/global-flexible-array-member.c b/clang/test/BoundsSafety/Sema/global-flexible-array-member.c new file mode 100644 index 0000000000000..8e435c0876ca7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-flexible-array-member.c @@ -0,0 +1,143 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + + +struct Inner { + int dummy; + int len; +}; +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; // expected-note 2{{initialized flexible array member 'fam' is here}} +}; + +struct Outer a = {{0, 2}, {3,5}}; +void assign_global_fam() { + a = a; // expected-error{{-fbounds-safety forbids passing 'struct Outer' by copy because it has a flexible array member}} + a.hdr.len = 1; +} +void assign_init_list_expr() { + a = (struct Outer){{0,33}, {7,11}}; // expected-error{{initialization of flexible array member is not allowed}} + a.hdr.len = 1; +} + +struct Outer b = {.hdr = {.len = 22}}; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 22}} +struct Outer c = {.hdr = {.dummy = 44}}; + +struct { + int len; + char fam[__counted_by(len)]; +} d = { .len = 2, .fam = {1,2} }; + +struct Outer e; +void assign_global_to_global() { + e = a; // expected-error{{-fbounds-safety forbids passing 'struct Outer' by copy because it has a flexible array member}} +} + +struct Middle { + int dummy; + struct Inner next; +}; + +struct Deep { + struct Middle hdr; + char fam[__counted_by(hdr.next.len)]; // expected-note 8{{initialized flexible array member 'fam' is here}} +}; + +// if these stop emitting errors about compile-time constants it's time to add a non-const version of each case +const struct Deep f = {{-5, {-1, 1}}, {99}}; +struct Deep g = {f.hdr, {98}}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep h = {{-6, a.hdr}, {97}}; // expected-error{{initializer element is not a compile-time constant}} + +extern struct Inner i; +struct Deep j = {{-7, i}, {96}}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep k = {{-8, {-3, f.hdr.next.len}}, f.fam[0]}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep l = {{-9, {-4, (float)f.hdr.next.len}}, f.fam[0]}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep m = {{-10, {-5, 1}}, f.fam}; // expected-error{{initializer element is not a compile-time constant}} + // expected-error@-1{{incompatible pointer to integer conversion initializing 'char' with an expression of type 'char const[__counted_by(hdr.next.len)]' (aka 'const char[]')}} +struct Deep n = {{-11, {-6, 2}}, .fam = f.fam}; // expected-error{{flexible array requires brace-enclosed initializer}} +struct Deep o = f; // expected-error{{initializer element is not a compile-time constant}} + +struct Deep p = {{-12, {-7, 1.0}}, {95}}; // expected-error{{count 'hdr.next.len' has non-integer value '1.' of type 'double'}} + +// check that we handle misformed initializer lists gracefully +struct Deep q = {{-13, {-8, i}}, {94}}; // expected-error{{initializing 'int' with an expression of incompatible type 'struct Inner'}} +struct Deep r = {i, {93}}; // expected-error{{initializing 'int' with an expression of incompatible type 'struct Inner'}} +struct Deep s = {{-14, -9, 1, 42}, {92}}; // expected-warning{{excess elements in struct initializer}} +struct Deep t = {{-15, -10, 1}, {91, 90}}; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 1}} +struct Deep u = {.len = 1, -16, -11, 89}; // expected-error{{field designator 'len' does not refer to any field in type 'struct Deep'}} + +// check that we find the right field in more obscure initializers +struct Deep v = {-17, -12, 2, 42}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} +struct Deep w = {{-18, .next.len = 2}, {88}}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} + +struct CastInCount { + float len; + char fam[__counted_by((int)len)]; // expected-note{{initialized flexible array member 'fam' is here}} +}; +struct CastInCount x = {2.5, {87}}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} + +typedef union { + int i; + float f; +} U; +struct UnionCount { + U len; + char fam[__counted_by(len.i)]; // expected-error{{count parameter refers to union 'len' of type 'U'}} + // expected-note@-1{{initialized flexible array member 'fam' is here}} +}; +struct UnionCount y = {.len.f = 3.5, {86}}; // expected-error{{count 'len.i' has non-integer value '3.5' of type 'double'}} + +struct Deep ø = { + .hdr.next.len = 2, + .fam = {85,84,83} // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 2}} +}; + +struct Deep ã = { + .hdr.next.len = 4, + .fam = {82,81,80} // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 4}} +}; + +struct AnonBitfield { + int : 10; + struct { + int : 11; + struct { + int : 12; + struct { + int : 13; + int asdf; + }; + struct { + int : 14; + int : 15; + }; + struct { + int : 16; + int nonAnon: 17; + int len; + }; + }; + int dummy; + } a; + char fam[__counted_by(a.len)]; // expected-note{{initialized flexible array member 'fam' is here}} +}; + +struct AnonBitfield z = { 1, {}, 2, 3, 4, {5, 6, 7}}; +struct AnonBitfield å = { 1, {}, 2, 3, 4, {5, 6}}; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 3}} +struct AnonBitfield ä = { 1, 2, 3, 4, {5, 6}}; // expected-error{{initializer for aggregate with no elements requires explicit braces}} + // expected-warning@-1{{excess elements in scalar initializer}} +struct AnonBitfield ö = { { { {1}, {}, {2, 3} }, 4}, {5, 6, 7}}; +struct AnonBitfield æ = { 1, {}, 2, 3, 4, 5, 6, 7 }; + +struct ASDF { + int asdf; + struct Deep footer; +}; +struct ASDF ß = { + // This error is not a -fbounds-safety limitation, but a general clang limitation that only allows initializing top level FAMs + .footer = { .hdr.next.len = 2, .fam = {82, 81} } // expected-error{{initialization of flexible array member is not allowed}} +}; diff --git a/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c b/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c new file mode 100644 index 0000000000000..dd780c598aea5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify -Wshorten-64-to-32 -triple x86_64 -fsyntax-only %s + +#include +#include + +void foo(int *__counted_by(size) buf, uint32_t size); + +void bar(int *__sized_by(len) arr, uint64_t len) { + foo(arr, len); // expected-warning{{implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long') to 'uint32_t' (aka 'unsigned int')}} +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c new file mode 100644 index 0000000000000..e0607c5d73a9d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +// This is a test for +// "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable"'s +// behavior when the `__ptrcheck_abi_assume_*()` macros are used. +// +// rdar://91980829 +// Currently this warning is not emitted for the code in this test. Ideally we +// would emit warnings here but the current implementation looks for +// `attr::PtrAutoAttr` to make sure we don't warn on assignment to local +// variables that are implicitly `__bidi_indexable`. Unfortunately we see +// exactly the same attribute for ABI visible pointers that are implicitly +// annotated using __ptrcheck_abi_assume_bidi_indexable() or +// __ptrcheck_abi_assume_indexable(). Thus we cannot distinguish them and so no +// warnings are emitted. + +// expected-no-diagnostics + +#include +extern int *__single explicitly_single0; + +//-------------------------------------------------------------------------- +// Assume __bidi_indexable on ABI pointers +//-------------------------------------------------------------------------- +__ptrcheck_abi_assume_bidi_indexable(); + +void take_bidi0(int *b_arg0); +int *implicitly_bidi0; + +typedef struct { + int *field; +} BidiStruct_t; + +void use_bidi(void) { + // Initialization + BidiStruct_t bidi = {.field = explicitly_single0}; + // Assignment + implicitly_bidi0 = explicitly_single0; + bidi.field = explicitly_single0; + // Argument pass + take_bidi0(explicitly_single0); +} + +int *no_warn_ret_bidi(void) { + return explicitly_single0; +} + +//-------------------------------------------------------------------------- +// Assume __indexable on ABI pointers +//-------------------------------------------------------------------------- +__ptrcheck_abi_assume_indexable(); + +void take_idx0(int *i_arg0); +int *implicitly_idx0; + +typedef struct { + int *field; +} IdxStruct_t; + +void use_idx(void) { + // Initialization + IdxStruct_t idx = {.field = explicitly_single0}; + // Assignment + implicitly_idx0 = explicitly_single0; + idx.field = explicitly_single0; + // Argument pass + take_idx0(explicitly_single0); +} + +int *no_warn_ret_idx(void) { + return explicitly_single0; +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c new file mode 100644 index 0000000000000..9c46183536122 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c @@ -0,0 +1,505 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +// Nested pointer variant of +// `implicit-single-to-explicit-indexable-conversion.c`. The diagnostics for +// this variant explicitly call out the fact the source type is `__single` and +// that the destination type is `__bidi_indexable` or `__indexable`. This is +// done so that it is more obvious that the important part is the attribute on +// the outer most pointer. + +//-------------------------------------------------------------------------- +// Functions taking a `__bidi_indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'b_arg0' here}} +void take_bidi0(int **__bidi_indexable b_arg0); +// expected-note@+1{{passing argument to parameter 'b_arg1' here}} +void take_bidi1(int **__bidi_indexable b_arg1); +// expected-note@+1{{passing argument to parameter 'b_arg2' here}} +void take_bidi2(int **__bidi_indexable b_arg2); +// expected-note@+1{{passing argument to parameter 'b_arg3' here}} +void take_bidi3(int **__bidi_indexable b_arg3); +// expected-note@+1{{passing argument to parameter 'b_arg4' here}} +void take_bidi4(int **__bidi_indexable b_arg4); +void take_bidi5(int **__bidi_indexable b_arg5); + +//-------------------------------------------------------------------------- +// Functions taking an `__indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'i_arg0' here}} +void take_idx0(int **__indexable i_arg0); +// expected-note@+1{{passing argument to parameter 'i_arg1' here}} +void take_idx1(int **__indexable i_arg1); +// expected-note@+1{{passing argument to parameter 'i_arg2' here}} +void take_idx2(int **__indexable i_arg2); +// expected-note@+1{{passing argument to parameter 'i_arg3' here}} +void take_idx3(int **__indexable i_arg3); +// expected-note@+1{{passing argument to parameter 'i_arg4' here}} +void take_idx4(int **__indexable i_arg4); +void take_idx5(int **__indexable i_arg5); + +//-------------------------------------------------------------------------- +// Pointers marked explicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int **__single explicitly_single0; +// expected-note@+1{{pointer 'explicitly_single1' declared here}} +extern int **__single explicitly_single1; +extern int **__single explicitly_single2; +// expected-note@+1{{pointer 'explicitly_single3' declared here}} +extern int **__single explicitly_single3; +extern int **__single explicitly_single4; +// expected-note@+1{{pointer 'explicitly_single5' declared here}} +extern int **__single explicitly_single5; +extern int **__single explicitly_single6; +// expected-note@+1{{pointer 'explicitly_single7' declared here}} +extern int **__single explicitly_single7; +extern int **__single explicitly_single8; +extern int **__single explicitly_single9; +// expected-note@+1{{pointer 'explicitly_single10' declared here}} +extern int **__single explicitly_single10; +extern int **__single explicitly_single11; +// expected-note@+1{{pointer 'explicitly_single12' declared here}} +extern int **__single explicitly_single12; +extern int **__single explicitly_single13; +// expected-note@+1{{pointer 'explicitly_single14' declared here}} +extern int **__single explicitly_single14; +extern int **__single explicitly_single15; +// expected-note@+1{{pointer 'explicitly_single16' declared here}} +extern int **__single explicitly_single16; +extern int **__single explicitly_single17; +// expected-note@+1{{pointer 'explicitly_single18' declared here}} +extern int **__single explicitly_single18; +extern int **__single explicitly_single19; +// expected-note@+1{{pointer 'explicitly_single20' declared here}} +extern int **__single explicitly_single20; +extern int **__single explicitly_single21; +// expected-note@+1{{pointer 'explicitly_single22' declared here}} +extern int **__single explicitly_single22; +extern int **__single explicitly_single23; +// expected-note@+1{{pointer 'explicitly_single24' declared here}} +extern int **__single explicitly_single24; +extern int **__single explicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers marked implicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int **implicitly_single0; +// expected-note@+1{{pointer 'implicitly_single1' declared here}} +extern int **implicitly_single1; +extern int **implicitly_single2; +// expected-note@+1{{pointer 'implicitly_single3' declared here}} +extern int **implicitly_single3; +extern int **implicitly_single4; +// expected-note@+1{{pointer 'implicitly_single5' declared here}} +extern int **implicitly_single5; +extern int **implicitly_single6; +// expected-note@+1{{pointer 'implicitly_single7' declared here}} +extern int **implicitly_single7; +extern int **implicitly_single8; +extern int **implicitly_single9; +// expected-note@+1{{pointer 'implicitly_single10' declared here}} +extern int **implicitly_single10; +extern int **implicitly_single11; +// expected-note@+1{{pointer 'implicitly_single12' declared here}} +extern int **implicitly_single12; +extern int **implicitly_single13; +// expected-note@+1{{pointer 'implicitly_single14' declared here}} +extern int **implicitly_single14; +extern int **implicitly_single15; +// expected-note@+1{{pointer 'implicitly_single16' declared here}} +extern int **implicitly_single16; +extern int **implicitly_single17; +// expected-note@+1{{pointer 'implicitly_single18' declared here}} +extern int **implicitly_single18; +extern int **implicitly_single19; +// expected-note@+1{{pointer 'implicitly_single20' declared here}} +extern int **implicitly_single20; +extern int **implicitly_single21; +// expected-note@+1{{pointer 'implicitly_single22' declared here}} +extern int **implicitly_single22; +extern int **implicitly_single23; +// expected-note@+1{{pointer 'implicitly_single24' declared here}} +extern int **implicitly_single24; +extern int **implicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers that will cause a bitcast as well as a -fbounds-safety attribute cast. +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +typedef unsigned long uint64_t; +_Static_assert(sizeof(uint64_t) > sizeof(int), "type must be larger than int"); +extern uint64_t **__single bigger_explicit_single0; +// expected-note@+1{{pointer 'bigger_explicit_single1' declared here}} +extern uint64_t **__single bigger_explicit_single1; +extern uint64_t **__single bigger_explicit_single2; +// expected-note@+1{{pointer 'bigger_explicit_single3' declared here}} +extern uint64_t **__single bigger_explicit_single3; +extern uint64_t **__single bigger_explicit_single4; +// expected-note@+1{{pointer 'bigger_explicit_single5' declared here}} +extern uint64_t **__single bigger_explicit_single5; +extern uint64_t **__single bigger_explicit_single6; +// expected-note@+1{{pointer 'bigger_explicit_single7' declared here}} +extern uint64_t **__single bigger_explicit_single7; +extern uint64_t **__single bigger_explicit_single8; +extern uint64_t **__single bigger_explicit_single9; +// expected-note@+1{{'bigger_explicit_single10' declared here}} +extern uint64_t **__single bigger_explicit_single10; +extern uint64_t **__single bigger_explicit_single11; +// expected-note@+1{{pointer 'bigger_explicit_single12' declared here}} +extern uint64_t **__single bigger_explicit_single12; +extern uint64_t **__single bigger_explicit_single13; +// expected-note@+1{{pointer 'bigger_explicit_single14' declared here}} +extern uint64_t **__single bigger_explicit_single14; +extern uint64_t **__single bigger_explicit_single15; +// expected-note@+1{{pointer 'bigger_explicit_single16' declared here}} +extern uint64_t **__single bigger_explicit_single16; +extern uint64_t **__single bigger_explicit_single17; +// expected-note@+1{{pointer 'bigger_explicit_single18' declared here}} +extern uint64_t **__single bigger_explicit_single18; +extern uint64_t **__single bigger_explicit_single19; +// expected-note@+1{{pointer 'bigger_explicit_single20' declared here}} +extern uint64_t **__single bigger_explicit_single20; +extern uint64_t **__single bigger_explicit_single21; +// expected-note@+1{{pointer 'bigger_explicit_single22' declared here}} +extern uint64_t **__single bigger_explicit_single22; +extern uint64_t **__single bigger_explicit_single23; +// expected-note@+1{{pointer 'bigger_explicit_single24' declared here}} +extern uint64_t **__single bigger_explicit_single24; +extern uint64_t **__single bigger_explicit_single25; + +//-------------------------------------------------------------------------- +// SINGLE_EXPR +// +// This macro expands to an expression that isn't a DeclRefExpr but still +// but is still __single. Uses of this macro when implicitly cast to an +// indexable pointer should not suggest adding +// the `__counted_by` attribute. +//-------------------------------------------------------------------------- +int dummy_elements[] = {0, 1, 2, 3}; +int *dummy_array[] = { + &dummy_elements[0], + &dummy_elements[1], + &dummy_elements[2], + &dummy_elements[3]}; +// SINGLE_EXPR isn't a DeclRefExpr so when this is implicitly cast we shouldn't +// suggest adding `__counted_by` to non existing DeclRefExpr. +#define SINGLE_EXPR ((int **__single)(&dummy_array[1])) + +//-------------------------------------------------------------------------- +// Function that return a __single pointer +// +// TODO(dliew): We should teach clang to produce a diagnostic for this case +// that suggests adding an attribute to the function return type. +//-------------------------------------------------------------------------- +int **__single returns_explicit_single(void); + +typedef struct { + int **__bidi_indexable field; +} BidiStruct_t; + +typedef struct { + int **__indexable field; +} IdxStruct_t; + +void use(void) { + //-------------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------------- + int **implicit_bidi = explicitly_single0; // no warning + int **implicit_bidi2 = implicitly_single0; // no warning + int **implicit_bidi3 = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single')}} + int **implicit_bidi4 = bigger_explicit_single0; + int **implicit_bidi5 = returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single1'}} + int **__bidi_indexable explicit_bidi = explicitly_single1; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single1'}} + int **__bidi_indexable explicit_bidi2 = implicitly_single1; + // expected-warning-re@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int **__bidi_indexable explicit_bidi3 = SINGLE_EXPR; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single1'}} + int **__bidi_indexable explicit_bidi4 = bigger_explicit_single1; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + int **__bidi_indexable explicit_bidi5 = returns_explicit_single(); + int **__bidi_indexable explicit_bidi6 = (int **__bidi_indexable)explicitly_single2; // no warning + int **__bidi_indexable explicit_bidi7 = (int **__bidi_indexable)implicitly_single2; // no warning + int **__bidi_indexable explicit_bidi8 = (int **__bidi_indexable)SINGLE_EXPR; // no warning + int **__bidi_indexable explicit_bidi9 = (int **__bidi_indexable)bigger_explicit_single2; // no warning + int **__bidi_indexable explicit_bidi10 = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single3'}} + int **__indexable explicit_idx = explicitly_single3; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single3'}} + int **__indexable explicit_idx2 = implicitly_single3; + // expected-warning-re@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int **__indexable explicit_idx3 = SINGLE_EXPR; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single3'}} + int **__indexable explicit_idx4 = bigger_explicit_single3; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + int **__indexable explicit_idx5 = returns_explicit_single(); + int **__indexable explicit_idx6 = (int **__indexable)explicitly_single4; // no warning + int **__indexable explicit_idx7 = (int **__indexable)implicitly_single4; // no warning + int **__indexable explicit_idx8 = (int **__indexable)SINGLE_EXPR; // no warning + int **__indexable explicit_idx9 = (int **__indexable)bigger_explicit_single4; // no warning + int **__indexable explicit_idx10 = (int **__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single5'}} + BidiStruct_t bidiStruct = {.field = explicitly_single5}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single5'}} + BidiStruct_t bidiStruct2 = {.field = implicitly_single5}; + // expected-warning-re@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + BidiStruct_t bidiStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single5'}} + BidiStruct_t bidiStruct4 = {.field = bigger_explicit_single5}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + BidiStruct_t bidiStruct5 = {.field = returns_explicit_single()}; + BidiStruct_t bidiStruct6 = {.field = (int **__bidi_indexable)explicitly_single6}; // no warning + BidiStruct_t bidiStruct7 = {.field = (int **__bidi_indexable)implicitly_single6}; // no warning + BidiStruct_t bidiStruct8 = {.field = (int **__bidi_indexable)SINGLE_EXPR}; // no warning + BidiStruct_t bidiStruct9 = {.field = (int **__bidi_indexable)bigger_explicit_single6}; // no warning + BidiStruct_t bidiStruct10 = {.field = (int **__bidi_indexable)returns_explicit_single()}; // no warning + + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single7'}} + IdxStruct_t idxStruct = {.field = explicitly_single7}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single7'}} + IdxStruct_t idxStruct2 = {.field = implicitly_single7}; + // expected-warning-re@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + IdxStruct_t idxStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single7'}} + IdxStruct_t idxStruct4 = {.field = bigger_explicit_single7}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + IdxStruct_t idxStruct5 = {.field = returns_explicit_single()}; + IdxStruct_t idxStruct6 = {.field = (int **__indexable)explicitly_single8}; // no warning + IdxStruct_t idxStruct7 = {.field = (int **__indexable)implicitly_single8}; // no warning + IdxStruct_t idxStruct8 = {.field = (int **__indexable)SINGLE_EXPR}; // no warning + IdxStruct_t idxStruct9 = {.field = (int **__indexable)bigger_explicit_single8}; // no warning + IdxStruct_t idxStruct10 = {.field = (int **__indexable)returns_explicit_single()}; // no warning + + //-------------------------------------------------------------------------- + // Assignment + //-------------------------------------------------------------------------- + implicit_bidi = implicitly_single9; // no warning + implicit_bidi = explicitly_single9; // no warning + implicit_bidi = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types assigning to 'int *__single*__bidi_indexable' from 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single')}} + implicit_bidi = bigger_explicit_single9; + implicit_bidi = returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single10'}} + explicit_bidi = implicitly_single10; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single10'}} + explicit_bidi = explicitly_single10; + // expected-warning-re@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_bidi = SINGLE_EXPR; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single10'}} + explicit_bidi = bigger_explicit_single10; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_bidi = returns_explicit_single(); + + explicit_bidi = (int **__bidi_indexable)implicitly_single11; // no warning + explicit_bidi = (int **__bidi_indexable)explicitly_single11; // no warning + explicit_bidi = (int **__bidi_indexable)SINGLE_EXPR; // no warning + explicit_bidi = (int **__bidi_indexable)bigger_explicit_single11; // no warning + explicit_bidi = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single24'}} + explicit_idx = implicitly_single24; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single24'}} + explicit_idx = explicitly_single24; + // expected-warning-re@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_idx = SINGLE_EXPR; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single24'}} + explicit_idx = bigger_explicit_single24; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_idx = returns_explicit_single(); + + explicit_idx = (int **__indexable)implicitly_single25; // no warning + explicit_idx = (int **__indexable)explicitly_single25; // no warning + explicit_idx = (int **__indexable)SINGLE_EXPR; // no warning + explicit_idx = (int **__indexable)bigger_explicit_single25; // no warning + explicit_idx = (int **__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single12'}} + bidiStruct.field = implicitly_single12; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single12'}} + bidiStruct.field = explicitly_single12; + // expected-warning-re@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + bidiStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single12'}} + bidiStruct.field = bigger_explicit_single12; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidiStruct.field = returns_explicit_single(); + bidiStruct.field = (int **__bidi_indexable)implicitly_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)explicitly_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)SINGLE_EXPR; // no warning + bidiStruct.field = (int **__bidi_indexable)bigger_explicit_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single14'}} + idxStruct.field = implicitly_single14; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single14'}} + idxStruct.field = explicitly_single14; + // expected-warning-re@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + idxStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single14'}} + idxStruct.field = bigger_explicit_single14; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + idxStruct.field = returns_explicit_single(); + idxStruct.field = (int **__indexable)implicitly_single15; // no warning + idxStruct.field = (int **__indexable)explicitly_single15; // no warning + idxStruct.field = (int **__indexable)SINGLE_EXPR; // no warning + idxStruct.field = (int **__indexable)bigger_explicit_single15; // no warning + idxStruct.field = (int **__indexable)returns_explicit_single(); // no warning + + //-------------------------------------------------------------------------- + // Argument pass + //-------------------------------------------------------------------------- + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single16'}} + take_bidi0(implicitly_single16); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single16'}} + take_bidi1(explicitly_single16); + // expected-warning-re@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_bidi2(SINGLE_EXPR); + // expected-warning@+1{{passing __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single16'}} + take_bidi3(bigger_explicit_single16); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + take_bidi4(returns_explicit_single()); + + take_bidi5((int **__bidi_indexable)implicitly_single17); // no warning + take_bidi5((int **__bidi_indexable)explicitly_single17); // no warning + take_bidi5((int **__bidi_indexable)SINGLE_EXPR); // no warning + take_bidi5((int **__bidi_indexable)bigger_explicit_single17); // no warning + take_bidi5((int **__bidi_indexable)returns_explicit_single()); // no warning + + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single18'}} + take_idx0(implicitly_single18); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single18'}} + take_idx1(explicitly_single18); + // expected-warning-re@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_idx2(SINGLE_EXPR); + // expected-warning@+1{{passing __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single18'}} + take_idx3(bigger_explicit_single18); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + take_idx4(returns_explicit_single()); + + take_idx5((int **__indexable)implicitly_single19); // no warning + take_idx5((int **__indexable)explicitly_single19); // no warning + take_idx5((int **__indexable)SINGLE_EXPR); // no warning + take_idx5((int **__indexable)bigger_explicit_single19); // no warning + take_idx5((int **__indexable)returns_explicit_single()); // no warning +} + +//-------------------------------------------------------------------------- +// Implicit conversion on return +//-------------------------------------------------------------------------- +int **__bidi_indexable warn_ret_bidi_expl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single20'}} + return explicitly_single20; +} + +int **__bidi_indexable warn_ret_bidi_impl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single20'}} + return implicitly_single20; +} + +int **__bidi_indexable warn_ret_bidi_single_expr(void) { + // expected-warning-re@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int **__bidi_indexable warn_ret_bidi_bigger_explicit(void) { + // expected-warning@+1{{returning __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single20'}} + return bigger_explicit_single20; +} + +int **__bidi_indexable warn_ret_bidi_tail_call_ret_single(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int **__bidi_indexable no_warn_ret_bidi_expl(void) { + return (int **__bidi_indexable)explicitly_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_impl(void) { + return (int **__bidi_indexable)implicitly_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_single_expr(void) { + return (int **__bidi_indexable)SINGLE_EXPR; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_bigger_explicit(void) { + return (int **__bidi_indexable)bigger_explicit_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_tail_call_ret_single(void) { + return (int **__bidi_indexable)returns_explicit_single(); // no warning +} + +int **__indexable warn_ret_idx_expl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single22'}} + return explicitly_single22; +} + +int **__indexable warn_ret_idx_impl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single22'}} + return implicitly_single22; +} + +int **__indexable warn_ret_idx_single_expr(void) { + // expected-warning-re@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int **__indexable warn_ret_idx_bigger_explicit(void) { + // expected-warning@+1{{returning __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single22'}} + return bigger_explicit_single22; +} + +int **__indexable warn_ret_idx_tail_call_ret_single(void) { + // expected-warning@+1{{eturning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int **__indexable no_warn_ret_idx_expl(void) { + return (int **__indexable)explicitly_single23; // no warning +} + +int **__indexable no_warn_ret_idx_impl(void) { + return (int **__indexable)implicitly_single23; // no warning +} + +int **__indexable no_warn_ret_idx_single_expr(void) { + return (int **__indexable)SINGLE_EXPR; // no warning +} + +int **__indexable no_warn_ret_idx_bigger_explicit(void) { + return (int **__indexable)bigger_explicit_single23; // no warning +} + +int **__indexable no_warn_ret_idx_tail_call_ret_single(void) { + return (int **__indexable)returns_explicit_single(); // no warning +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c new file mode 100644 index 0000000000000..65e5fe70651c4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c @@ -0,0 +1,495 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s + +#include + +//-------------------------------------------------------------------------- +// Functions taking a `__bidi_indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'b_arg0' here}} +void take_bidi0(int *__bidi_indexable b_arg0); +// expected-note@+1{{passing argument to parameter 'b_arg1' here}} +void take_bidi1(int *__bidi_indexable b_arg1); +// expected-note@+1{{passing argument to parameter 'b_arg2' here}} +void take_bidi2(int *__bidi_indexable b_arg2); +// expected-note@+1{{passing argument to parameter 'b_arg3' here}} +void take_bidi3(int *__bidi_indexable b_arg3); +// expected-note@+1{{passing argument to parameter 'b_arg4' here}} +void take_bidi4(int *__bidi_indexable b_arg4); +void take_bidi5(int *__bidi_indexable b_arg5); + +//-------------------------------------------------------------------------- +// Functions taking an `__indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'i_arg0' here}} +void take_idx0(int *__indexable i_arg0); +// expected-note@+1{{passing argument to parameter 'i_arg1' here}} +void take_idx1(int *__indexable i_arg1); +// expected-note@+1{{passing argument to parameter 'i_arg2' here}} +void take_idx2(int *__indexable i_arg2); +// expected-note@+1{{passing argument to parameter 'i_arg3' here}} +void take_idx3(int *__indexable i_arg3); +// expected-note@+1{{passing argument to parameter 'i_arg4' here}} +void take_idx4(int *__indexable i_arg4); +void take_idx5(int *__indexable i_arg5); + +//-------------------------------------------------------------------------- +// Pointers marked explicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int *__single explicitly_single0; +// expected-note@+1{{pointer 'explicitly_single1' declared here}} +extern int *__single explicitly_single1; +extern int *__single explicitly_single2; +// expected-note@+1{{pointer 'explicitly_single3' declared here}} +extern int *__single explicitly_single3; +extern int *__single explicitly_single4; +// expected-note@+1{{pointer 'explicitly_single5' declared here}} +extern int *__single explicitly_single5; +extern int *__single explicitly_single6; +// expected-note@+1{{pointer 'explicitly_single7' declared here}} +extern int *__single explicitly_single7; +extern int *__single explicitly_single8; +extern int *__single explicitly_single9; +// expected-note@+1{{pointer 'explicitly_single10' declared here}} +extern int *__single explicitly_single10; +extern int *__single explicitly_single11; +// expected-note@+1{{pointer 'explicitly_single12' declared here}} +extern int *__single explicitly_single12; +extern int *__single explicitly_single13; +// expected-note@+1{{pointer 'explicitly_single14' declared here}} +extern int *__single explicitly_single14; +extern int *__single explicitly_single15; +// expected-note@+1{{pointer 'explicitly_single16' declared here}} +extern int *__single explicitly_single16; +extern int *__single explicitly_single17; +// expected-note@+1{{pointer 'explicitly_single18' declared here}} +extern int *__single explicitly_single18; +extern int *__single explicitly_single19; +// expected-note@+1{{pointer 'explicitly_single20' declared here}} +extern int *__single explicitly_single20; +extern int *__single explicitly_single21; +// expected-note@+1{{pointer 'explicitly_single22' declared here}} +extern int *__single explicitly_single22; +extern int *__single explicitly_single23; +// expected-note@+1{{pointer 'explicitly_single24' declared here}} +extern int *__single explicitly_single24; +extern int *__single explicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers marked implicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int *implicitly_single0; +// expected-note@+1{{pointer 'implicitly_single1' declared here}} +extern int *implicitly_single1; +extern int *implicitly_single2; +// expected-note@+1{{pointer 'implicitly_single3' declared here}} +extern int *implicitly_single3; +extern int *implicitly_single4; +// expected-note@+1{{pointer 'implicitly_single5' declared here}} +extern int *implicitly_single5; +extern int *implicitly_single6; +// expected-note@+1{{pointer 'implicitly_single7' declared here}} +extern int *implicitly_single7; +extern int *implicitly_single8; +extern int *implicitly_single9; +// expected-note@+1{{pointer 'implicitly_single10' declared here}} +extern int *implicitly_single10; +extern int *implicitly_single11; +// expected-note@+1{{pointer 'implicitly_single12' declared here}} +extern int *implicitly_single12; +extern int *implicitly_single13; +// expected-note@+1{{pointer 'implicitly_single14' declared here}} +extern int *implicitly_single14; +extern int *implicitly_single15; +// expected-note@+1{{pointer 'implicitly_single16' declared here}} +extern int *implicitly_single16; +extern int *implicitly_single17; +// expected-note@+1{{pointer 'implicitly_single18' declared here}} +extern int *implicitly_single18; +extern int *implicitly_single19; +// expected-note@+1{{pointer 'implicitly_single20' declared here}} +extern int *implicitly_single20; +extern int *implicitly_single21; +// expected-note@+1{{pointer 'implicitly_single22' declared here}} +extern int *implicitly_single22; +extern int *implicitly_single23; +// expected-note@+1{{pointer 'implicitly_single24' declared here}} +extern int *implicitly_single24; +extern int *implicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers that will cause a bitcast as well as a -fbounds-safety attribute cast. +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +typedef unsigned long uint64_t; +_Static_assert(sizeof(uint64_t) > sizeof(int), "type must be larger than int"); +extern uint64_t *__single bigger_explicit_single0; +// expected-note@+1{{pointer 'bigger_explicit_single1' declared here}} +extern uint64_t *__single bigger_explicit_single1; +extern uint64_t *__single bigger_explicit_single2; +// expected-note@+1{{pointer 'bigger_explicit_single3' declared here}} +extern uint64_t *__single bigger_explicit_single3; +extern uint64_t *__single bigger_explicit_single4; +// expected-note@+1{{pointer 'bigger_explicit_single5' declared here}} +extern uint64_t *__single bigger_explicit_single5; +extern uint64_t *__single bigger_explicit_single6; +// expected-note@+1{{pointer 'bigger_explicit_single7' declared here}} +extern uint64_t *__single bigger_explicit_single7; +extern uint64_t *__single bigger_explicit_single8; +extern uint64_t *__single bigger_explicit_single9; +// expected-note@+1{{'bigger_explicit_single10' declared here}} +extern uint64_t *__single bigger_explicit_single10; +extern uint64_t *__single bigger_explicit_single11; +// expected-note@+1{{pointer 'bigger_explicit_single12' declared here}} +extern uint64_t *__single bigger_explicit_single12; +extern uint64_t *__single bigger_explicit_single13; +// expected-note@+1{{pointer 'bigger_explicit_single14' declared here}} +extern uint64_t *__single bigger_explicit_single14; +extern uint64_t *__single bigger_explicit_single15; +// expected-note@+1{{pointer 'bigger_explicit_single16' declared here}} +extern uint64_t *__single bigger_explicit_single16; +extern uint64_t *__single bigger_explicit_single17; +// expected-note@+1{{pointer 'bigger_explicit_single18' declared here}} +extern uint64_t *__single bigger_explicit_single18; +extern uint64_t *__single bigger_explicit_single19; +// expected-note@+1{{pointer 'bigger_explicit_single20' declared here}} +extern uint64_t *__single bigger_explicit_single20; +extern uint64_t *__single bigger_explicit_single21; +// expected-note@+1{{pointer 'bigger_explicit_single22' declared here}} +extern uint64_t *__single bigger_explicit_single22; +extern uint64_t *__single bigger_explicit_single23; +// expected-note@+1{{pointer 'bigger_explicit_single24' declared here}} +extern uint64_t *__single bigger_explicit_single24; +extern uint64_t *__single bigger_explicit_single25; + +//-------------------------------------------------------------------------- +// SINGLE_EXPR +// +// This macro expands to an expression that isn't a DeclRefExpr but still +// but is still __single. Uses of this macro when implicitly cast to an +// indexable pointer should not suggest adding +// the `__counted_by` attribute. +//-------------------------------------------------------------------------- +int dummy_array[] = {0, 1, 2, 3}; +// SINGLE_EXPR isn't a DeclRefExpr so when this is implicitly cast we shouldn't +// suggest adding `__counted_by` to non existing DeclRefExpr. +#define SINGLE_EXPR ((int *__single)(&dummy_array[2])) + +//-------------------------------------------------------------------------- +// Function that return a __single pointer +// +// TODO(dliew): We should teach clang to produce a diagnostic for this case +// that suggests adding an attribute to the function return type +// (rdar://91928583). +//-------------------------------------------------------------------------- +int *__single returns_explicit_single(void); + +typedef struct { + int *__bidi_indexable field; +} BidiStruct_t; + +typedef struct { + int *__indexable field; +} IdxStruct_t; + +void use(void) { + //-------------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------------- + int *implicit_bidi = explicitly_single0; // no warning + int *implicit_bidi2 = implicitly_single0; // no warning + int *implicit_bidi3 = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types initializing 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single')}} + int *implicit_bidi4 = bigger_explicit_single0; + int *implicit_bidi5 = returns_explicit_single(); // no warning + int * _Nonnull implicit_bidi6 = returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single1}} + int *__bidi_indexable explicit_bidi = explicitly_single1; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single1}} + int *__bidi_indexable explicit_bidi2 = implicitly_single1; + // expected-warning-re@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int *__bidi_indexable explicit_bidi3 = SINGLE_EXPR; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single1'}} + int *__bidi_indexable explicit_bidi4 = bigger_explicit_single1; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + int *__bidi_indexable explicit_bidi5 = returns_explicit_single(); + int *__bidi_indexable explicit_bidi6 = (int *__bidi_indexable)explicitly_single2; // no warning + int *__bidi_indexable explicit_bidi7 = (int *__bidi_indexable)implicitly_single2; // no warning + int *__bidi_indexable explicit_bidi8 = (int *__bidi_indexable)SINGLE_EXPR; // no warning + int *__bidi_indexable explicit_bidi9 = (int *__bidi_indexable)bigger_explicit_single2; // no warning + int *__bidi_indexable explicit_bidi10 = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single3'}} + int *__indexable explicit_idx = explicitly_single3; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single3'}} + int *__indexable explicit_idx2 = implicitly_single3; + // expected-warning-re@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int *__indexable explicit_idx3 = SINGLE_EXPR; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single3'}} + int *__indexable explicit_idx4 = bigger_explicit_single3; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + int *__indexable explicit_idx5 = returns_explicit_single(); + int *__indexable explicit_idx6 = (int *__indexable)explicitly_single4; // no warning + int *__indexable explicit_idx7 = (int *__indexable)implicitly_single4; // no warning + int *__indexable explicit_idx8 = (int *__indexable)SINGLE_EXPR; // no warning + int *__indexable explicit_idx9 = (int *__indexable)bigger_explicit_single4; // no warning + int *__indexable explicit_idx10 = (int *__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single5'}} + BidiStruct_t bidiStruct = {.field = explicitly_single5}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single5'}} + BidiStruct_t bidiStruct2 = {.field = implicitly_single5}; + // expected-warning-re@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + BidiStruct_t bidiStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single5'}} + BidiStruct_t bidiStruct4 = {.field = bigger_explicit_single5}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + BidiStruct_t bidiStruct5 = {.field = returns_explicit_single()}; + BidiStruct_t bidiStruct6 = {.field = (int *__bidi_indexable)explicitly_single6}; // no warning + BidiStruct_t bidiStruct7 = {.field = (int *__bidi_indexable)implicitly_single6}; // no warning + BidiStruct_t bidiStruct8 = {.field = (int *__bidi_indexable)SINGLE_EXPR}; // no warning + BidiStruct_t bidiStruct9 = {.field = (int *__bidi_indexable)bigger_explicit_single6}; // no warning + BidiStruct_t bidiStruct10 = {.field = (int *__bidi_indexable)returns_explicit_single()}; // no warning + + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single7'}} + IdxStruct_t idxStruct = {.field = explicitly_single7}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single7'}} + IdxStruct_t idxStruct2 = {.field = implicitly_single7}; + // expected-warning-re@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + IdxStruct_t idxStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single7'}} + IdxStruct_t idxStruct4 = {.field = bigger_explicit_single7}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + IdxStruct_t idxStruct5 = {.field = returns_explicit_single()}; + IdxStruct_t idxStruct6 = {.field = (int *__indexable)explicitly_single8}; // no warning + IdxStruct_t idxStruct7 = {.field = (int *__indexable)implicitly_single8}; // no warning + IdxStruct_t idxStruct8 = {.field = (int *__indexable)SINGLE_EXPR}; // no warning + IdxStruct_t idxStruct9 = {.field = (int *__indexable)bigger_explicit_single8}; // no warning + IdxStruct_t idxStruct10 = {.field = (int *__indexable)returns_explicit_single()}; // no warning + + //-------------------------------------------------------------------------- + // Assignment + //-------------------------------------------------------------------------- + implicit_bidi = implicitly_single9; // no warning + implicit_bidi = explicitly_single9; // no warning + implicit_bidi = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types assigning to 'int *__bidi_indexable' from 'uint64_t *__single' (aka 'unsigned long *__single')}} + implicit_bidi = bigger_explicit_single9; + implicit_bidi = returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single10'}} + explicit_bidi = implicitly_single10; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single10'}} + explicit_bidi = explicitly_single10; + // expected-warning-re@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_bidi = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single10'}} + explicit_bidi = bigger_explicit_single10; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_bidi = returns_explicit_single(); + + explicit_bidi = (int *__bidi_indexable)implicitly_single11; // no warning + explicit_bidi = (int *__bidi_indexable)explicitly_single11; // no warning + explicit_bidi = (int *__bidi_indexable)SINGLE_EXPR; // no warning + explicit_bidi = (int *__bidi_indexable)bigger_explicit_single11; // no warning + explicit_bidi = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single24'}} + explicit_idx = implicitly_single24; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single24'}} + explicit_idx = explicitly_single24; + // expected-warning-re@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_idx = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single24'}} + explicit_idx = bigger_explicit_single24; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_idx = returns_explicit_single(); + + explicit_idx = (int *__indexable)implicitly_single25; // no warning + explicit_idx = (int *__indexable)explicitly_single25; // no warning + explicit_idx = (int *__indexable)SINGLE_EXPR; // no warning + explicit_idx = (int *__indexable)bigger_explicit_single25; // no warning + explicit_idx = (int *__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single12'}} + bidiStruct.field = implicitly_single12; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single12'}} + bidiStruct.field = explicitly_single12; + // expected-warning-re@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + bidiStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single12'}} + bidiStruct.field = bigger_explicit_single12; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidiStruct.field = returns_explicit_single(); + bidiStruct.field = (int *__bidi_indexable)implicitly_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)explicitly_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)SINGLE_EXPR; // no warning + bidiStruct.field = (int *__bidi_indexable)bigger_explicit_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single14'}} + idxStruct.field = implicitly_single14; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single14'}} + idxStruct.field = explicitly_single14; + // expected-warning-re@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + idxStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single14'}} + idxStruct.field = bigger_explicit_single14; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + idxStruct.field = returns_explicit_single(); + idxStruct.field = (int *__indexable)implicitly_single15; // no warning + idxStruct.field = (int *__indexable)explicitly_single15; // no warning + idxStruct.field = (int *__indexable)SINGLE_EXPR; // no warning + idxStruct.field = (int *__indexable)bigger_explicit_single15; // no warning + idxStruct.field = (int *__indexable)returns_explicit_single(); // no warning + + //-------------------------------------------------------------------------- + // Argument pass + //-------------------------------------------------------------------------- + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single16'}} + take_bidi0(implicitly_single16); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single16'}} + take_bidi1(explicitly_single16); + // expected-warning-re@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_bidi2(SINGLE_EXPR); + // expected-warning@+1{{passing type 'uint64_t *__single' (aka 'unsigned long *__single') to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single16'}} + take_bidi3(bigger_explicit_single16); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + take_bidi4(returns_explicit_single()); + + take_bidi5((int *__bidi_indexable)implicitly_single17); // no warning + take_bidi5((int *__bidi_indexable)explicitly_single17); // no warning + take_bidi5((int *__bidi_indexable)SINGLE_EXPR); // no warning + take_bidi5((int *__bidi_indexable)bigger_explicit_single17); // no warning + take_bidi5((int *__bidi_indexable)returns_explicit_single()); // no warning + + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single18'}} + take_idx0(implicitly_single18); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single18'}} + take_idx1(explicitly_single18); + // expected-warning-re@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_idx2(SINGLE_EXPR); + // expected-warning@+1{{passing type 'uint64_t *__single' (aka 'unsigned long *__single') to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single18'}} + take_idx3(bigger_explicit_single18); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + take_idx4(returns_explicit_single()); + + take_idx5((int *__indexable)implicitly_single19); // no warning + take_idx5((int *__indexable)explicitly_single19); // no warning + take_idx5((int *__indexable)SINGLE_EXPR); // no warning + take_idx5((int *__indexable)bigger_explicit_single19); // no warning + take_idx5((int *__indexable)returns_explicit_single()); // no warning +} + +//-------------------------------------------------------------------------- +// Implicit conversion on return +//-------------------------------------------------------------------------- +int *__bidi_indexable warn_ret_bidi_expl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single20'}} + return explicitly_single20; +} + +int *__bidi_indexable warn_ret_bidi_impl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single20'}} + return implicitly_single20; +} + +int *__bidi_indexable warn_ret_bidi_single_expr(void) { + // expected-warning-re@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int *__bidi_indexable warn_ret_bidi_bigger_explicit(void) { + // expected-warning@+1{{returning type 'uint64_t *__single' (aka 'unsigned long *__single') from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single20'}} + return bigger_explicit_single20; +} + +int *__bidi_indexable warn_ret_bidi_tail_call_ret_single(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int *__bidi_indexable no_warn_ret_bidi_expl(void) { + return (int *__bidi_indexable)explicitly_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_impl(void) { + return (int *__bidi_indexable)implicitly_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_single_expr(void) { + return (int *__bidi_indexable)SINGLE_EXPR; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_bigger_explicit(void) { + return (int *__bidi_indexable)bigger_explicit_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_tail_call_ret_single(void) { + return (int *__bidi_indexable)returns_explicit_single(); // no warning +} + +int *__indexable warn_ret_idx_expl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single22'}} + return explicitly_single22; +} + +int *__indexable warn_ret_idx_impl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single22'}} + return implicitly_single22; +} + +int *__indexable warn_ret_idx_single_expr(void) { + // expected-warning-re@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int *__indexable warn_ret_idx_bigger_explicit(void) { + // expected-warning@+1{{returning type 'uint64_t *__single' (aka 'unsigned long *__single') from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single22'}} + return bigger_explicit_single22; +} + +int *__indexable warn_ret_idx_tail_call_ret_single(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int *__indexable no_warn_ret_idx_expl(void) { + return (int *__indexable)explicitly_single23; // no warning +} + +int *__indexable no_warn_ret_idx_impl(void) { + return (int *__indexable)implicitly_single23; // no warning +} + +int *__indexable no_warn_ret_idx_single_expr(void) { + return (int *__indexable)SINGLE_EXPR; // no warning +} + +int *__indexable no_warn_ret_idx_bigger_explicit(void) { + return (int *__indexable)bigger_explicit_single23; // no warning +} + +int *__indexable no_warn_ret_idx_tail_call_ret_single(void) { + return (int *__indexable)returns_explicit_single(); // no warning +} diff --git a/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c b/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c new file mode 100644 index 0000000000000..b0a95ff04ca79 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +// This should get __sized_by_or_null(count * size). +void *my_calloc(unsigned count, unsigned size) + __attribute__((alloc_size(1, 2))); + +#define ALLOC(size) my_calloc(size, 1) + +void test(void *__sized_by(*out_len) * out, unsigned *out_len) { + *out = ALLOC(sizeof(int)); + *out_len = sizeof(int); +} diff --git a/clang/test/BoundsSafety/Sema/include-project-header.c b/clang/test/BoundsSafety/Sema/include-project-header.c new file mode 100644 index 0000000000000..9817c990f01b7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/include-project-header.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// expected-no-diagnostics + +#include +#include "mock-header.h" + +int main() { + // In this configuration, `returns_pointer` returns a __single pointer, so + // it converts to __bidi_indexable and dereferences properly. + int *p = returns_pointer(); + return *p; +} diff --git a/clang/test/BoundsSafety/Sema/include-system-header.c b/clang/test/BoundsSafety/Sema/include-system-header.c new file mode 100644 index 0000000000000..2a648bddfc17b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/include-system-header.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -DSYSTEM_HEADER -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -DSYSTEM_HEADER -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include "mock-header.h" + +int main() { + // In this configuration, `returns_pointer` returns an __unsafe_indexable + // pointer, so it _does not_ convert to a __bidi_indexable pointer. + int *p = returns_pointer(); // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + return *p; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c b/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c new file mode 100644 index 0000000000000..a9bb65af11ca2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +typedef struct lol { + int *x[3]; +} lol_t; + +lol_t *g; + +// expected-note@+1{{passing argument to parameter 'arg' here}} +void test_arg(int *__unsafe_indexable *__counted_by(3) arg); + +void test(void) { + // expected-error@+1{{initializing 'int *__unsafe_indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__single[3]'}} + int *__unsafe_indexable *__counted_by(3) m1 = g->x; + int *__unsafe_indexable *__counted_by(3) m2 = (int *__unsafe_indexable *)g->x; + + // expected-error@+1{{passing 'int *__single[3]' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*__single __counted_by(3)' (aka 'int *__unsafe_indexable*__single')}} + test_arg(g->x); + + int *__unsafe_indexable *pp1; + // expected-error@+1{{assigning to 'int *__unsafe_indexable*__bidi_indexable' from 'int *__single[3]'}} + pp1 = g->x; +} + +int *__unsafe_indexable * test_ret() { + // expected-error@+1{{returning 'int *__single[3]' from a function with result of incompatible nested pointer type 'int *__unsafe_indexable*__single'}} + return g->x; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c new file mode 100644 index 0000000000000..52d49edce89aa --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// expected-note@+1{{passing argument to parameter 'fp' here}} +int* foo(int **__unsafe_indexable fp); +// expected-note@+1{{passing argument to parameter 'fp' here}} +int* fooBound(int *__bidi_indexable fp); + +typedef int* (*bar_t)(int *__unsafe_indexable*__bidi_indexable); +typedef int* (*baz_t)(int **__single); + +void Test () { + // expected-note@+1{{pointer 'ptrThin' declared here}} + int *__single ptrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + int *__bidi_indexable *__single ptrBoundPtrThin = &ptrThin; + int *__single *__single ptrThinPtrThin = &ptrThin; // ok + int *__indexable *__single ptrArrayPtrThin; + + // expected-error@+1{{passing 'int *__indexable*__single' to parameter of incompatible nested pointer type 'int *__single*__unsafe_indexable'; use explicit cast to perform this conversion}} + foo(ptrArrayPtrThin); + foo(ptrThinPtrThin); // ok + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'ptrThin'}} + fooBound(ptrThin); + fooBound(*ptrBoundPtrThin); // ok + + long intVal = 0xdeadbeef; + int *__bidi_indexable ptr = (int *)intVal; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + ptr = intVal; + + // expected-error@+1{{incompatible integer to pointer conversion initializing 'int *__unsafe_indexable' with an expression of type 'long'}} + int *__unsafe_indexable ptrUnsafe = intVal; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + int *__single ptrThin2 = intVal; + + int **ptrPtr = (int**)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtr = ptr; + + int ***ptrPtrPtr = (int***)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtrPtr = ptr; + + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + bar_t fp1 = foo; + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + baz_t fp2 = foo; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c new file mode 100644 index 0000000000000..23f34b6c2edcf --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note +#include +#include "system-header-func-decl.h" + +int* fooBound(int *__bidi_indexable fp); + +void arrayFoo(int *(*__unsafe_indexable fp)[1]); + +typedef int* (*bar_t)(int *__unsafe_indexable*__bidi_indexable); +typedef int* (*baz_t)(int **__single); + +void Test () { + int *__single ptrThin; + int *__single (*__bidi_indexable ptrThinAPtrBound)[1]; + + // initializing + + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__bidi_indexable *__single ptrBoundPtrThin = &ptrThin; + int *__single *__bidi_indexable ptrThinPtrBound = &ptrThin; // ok + int *__single *__single ptrThinPtrThin = &ptrThin; // ok + // expected-error@+1{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__indexable *__single ptrArrayPtrThin = &ptrThin; + + // expected-error@+1{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__bidi_indexable(*__single ptrBoundAPtrThin)[1] = ptrThinAPtrBound; + int *__single(*__single ptrThinAPtrThin)[1] = ptrThinAPtrBound; + // expected-error@+1{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__indexable(*__single ptrArrayAPtrThin)[1] = ptrThinAPtrBound; + + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__bidi_indexable *__single ptrBoundPtrThin2 = ptrThinAPtrBound; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__bidi_indexable ptrThinPtrBound2 = ptrThinAPtrBound; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__single' with an expression of type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__single ptrThinPtrThin2 = ptrThinAPtrBound; + // expected-error@+1{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__indexable *__single ptrArrayPtrThin2 = ptrThinAPtrBound; + + // expected-error@+1{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__bidi_indexable(*__single ptrBoundAPtrThin2)[1] = &ptrThin; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single(*__bidi_indexable)[1]' with an expression of type 'int *__single*__bidi_indexable'}} + int *__single(*__bidi_indexable ptrThinAPtrBound2)[1] = &ptrThin; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single(*__single)[1]' with an expression of type 'int *__single*__bidi_indexable'}} + int *__single(*__single ptrThinAPtrThin2)[1] = &ptrThin; + // expected-error@+1{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__indexable(*__single ptrArrayAPtrThin2)[1] = &ptrThin; + + int *__bidi_indexable *__single *__bidi_indexable ptrBoundPtrThinPtrBound = &ptrBoundPtrThin; + int *__bidi_indexable *__single *__single ptrBoundPtrThinPtrThin = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__bidi_indexable *__indexable *__bidi_indexable ptrBoundPtrArrayPtrBound = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__indexable*__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__single *__bidi_indexable ptrArrayPtrThinPtrBound = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__indexable *__bidi_indexable ptrArrayPtrArrayPtrBound = &ptrBoundPtrThin; + + int *__bidi_indexable(*__single * __bidi_indexable ptrBoundAPtrThinPtrBound)[1] = &ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single ptrBoundAPtrThinPtrThin)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__bidi_indexable(*__indexable * __bidi_indexable ptrBoundAPtrArrayPtrBound)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__indexable(*__single*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__single * __bidi_indexable ptrArrayAPtrThinPtrBound)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__indexable * __bidi_indexable ptrArrayAPtrArrayPtrBound)[1] = &ptrBoundAPtrThin; + + // casting + + int *__bidi_indexable *__single ptrBoundPtrThin3 = (int *__bidi_indexable *__single) & ptrThin; + int *__bidi_indexable *__single implicitPtrBoundPtrThin3 = & ptrThin; // expected-error{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__single *__bidi_indexable ptrThinPtrBound3 = (int *__single *__bidi_indexable) & ptrThin; + int *__single *__bidi_indexable implicitPtrThinPtrBound3 = & ptrThin; + int *__single *__single ptrThinPtrThin3 = (int *__single *__single) & ptrThin; + int *__single *__single implicitPtrThinPtrThin3 = & ptrThin; + int *__indexable *__single ptrArrayPtrThin3 = (int *__indexable *__single) & ptrThin; + int *__indexable *__single implicitPtrArrayPtrThin3 = & ptrThin; // expected-error{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + + int *__bidi_indexable(*__single ptrBoundAPtrThin3)[1] = (int *__bidi_indexable(*__single)[1])ptrThinAPtrBound; + int *__bidi_indexable(*__single implicitPtrBoundAPtrThin3)[1] = ptrThinAPtrBound; // expected-error{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__single(*__bidi_indexable ptrThinAPtrBound3)[1] = (int *__single(*__bidi_indexable)[1])ptrThinAPtrBound; + int *__single(*__bidi_indexable implicitPtrThinAPtrBound3)[1] = ptrThinAPtrBound; + int *__single(*__single ptrThinAPtrThin3)[1] = (int *__single(*__single)[1])ptrThinAPtrBound; + int *__single(*__single implicitPtrThinAPtrThin3)[1] = ptrThinAPtrBound; + int *__indexable(*__single ptrArrayAPtrThin3)[1] = (int *__indexable(*__single)[1])ptrThinAPtrBound; + int *__indexable(*__single implicitPtrArrayAPtrThin3)[1] = ptrThinAPtrBound; // expected-error{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + + int *__bidi_indexable *__single ptrBoundPtrThin4 = (int *__bidi_indexable *__single)ptrThinAPtrBound; + int *__bidi_indexable *__single implicitPtrBoundPtrThin4 = ptrThinAPtrBound; // expected-error{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__bidi_indexable ptrThinPtrBound4 = (int *__single *__bidi_indexable)ptrThinAPtrBound; + int *__single *__bidi_indexable implicitPtrThinPtrBound4 = ptrThinAPtrBound; // expected-warning{{incompatible pointer types initializing}} + int *__single *__single ptrThinPtrThin4 = (int *__single *__single)ptrThinAPtrBound; + int *__single *__single implicitPtrThinPtrThin4 = ptrThinAPtrBound; // expected-warning{{incompatible pointer types initializing}} + int *__indexable *__single ptrArrayPtrThin4 = (int *__indexable *__single)ptrThinAPtrBound; + int *__indexable *__single implicitPtrArrayPtrThin4 = ptrThinAPtrBound; // expected-error{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + + int *__bidi_indexable(*__single ptrBoundAPtrThin4)[1] = (int *__bidi_indexable(*__single)[1]) & ptrThin; + int *__bidi_indexable(*__single implicitPtrBoundAPtrThin4)[1] = & ptrThin; // expected-error{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__single(*__bidi_indexable ptrThinAPtrBound4)[1] = (int *__single(*__bidi_indexable)[1]) & ptrThin; + int *__single(*__bidi_indexable implicitPtrThinAPtrBound4)[1] = & ptrThin; // expected-warning{{incompatible pointer types initializing}} + int *__single(*__single ptrThinAPtrThin4)[1] = (int *__single(*__single)[1]) & ptrThin; + int *__single(*__single implicitPtrThinAPtrThin4)[1] = & ptrThin; // expected-warning{{incompatible pointer types initializing}} + int *__indexable(*__single ptrArrayAPtrThin4)[1] = (int *__indexable(*__single)[1]) & ptrThin; + int *__indexable(*__single implicitPtrArrayAPtrThin4)[1] = & ptrThin; // expected-error{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + + int *__bidi_indexable *__single *__bidi_indexable ptrBoundPtrThinPtrBound2 = (int *__bidi_indexable *__single *__bidi_indexable) & ptrBoundPtrThin; + int *__bidi_indexable *__single *__bidi_indexable implicitPtrBoundPtrThinPtrBound2 = & ptrBoundPtrThin; + int *__bidi_indexable *__single *__single ptrBoundPtrThinPtrThin2 = (int *__bidi_indexable *__single *__single) & ptrBoundPtrThin; + int *__bidi_indexable *__single *__single implicitPtrBoundPtrThinPtrThin2 = & ptrBoundPtrThin; + int *__bidi_indexable *__indexable *__bidi_indexable ptrBoundPtrArrayPtrBound2 = (int *__bidi_indexable *__indexable *__bidi_indexable) & ptrBoundPtrThin; + int *__bidi_indexable *__indexable *__bidi_indexable implicitPtrBoundPtrArrayPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__bidi_indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__single *__bidi_indexable ptrArrayPtrThinPtrBound2 = (int *__indexable *__single *__bidi_indexable) & ptrBoundPtrThin; + int *__indexable *__single *__bidi_indexable implicitPtrArrayPtrThinPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__indexable*__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__indexable *__bidi_indexable ptrArrayPtrArrayPtrBound2 = (int *__indexable *__indexable *__bidi_indexable) & ptrBoundPtrThin; + int *__indexable *__indexable *__bidi_indexable implicitPtrArrayPtrArrayPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + + int *__bidi_indexable(*__single * __bidi_indexable ptrBoundAPtrThinPtrBound2)[1] = (int *__bidi_indexable(*__single * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __bidi_indexable implicitPtrBoundAPtrThinPtrBound2)[1] = & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single ptrBoundAPtrThinPtrThin2)[1] = (int *__bidi_indexable(*__single * __single)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single implicitPtrBoundAPtrThinPtrThin2)[1] = & ptrBoundAPtrThin; + int *__bidi_indexable(*__indexable * __bidi_indexable ptrBoundAPtrArrayPtrBound2)[1] = (int *__bidi_indexable(*__indexable * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__indexable * __bidi_indexable implicitPtrBoundAPtrArrayPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__bidi_indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__single * __bidi_indexable ptrArrayAPtrThinPtrBound2)[1] = (int *__indexable(*__single * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__indexable(*__single * __bidi_indexable implicitPtrArrayAPtrThinPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__indexable(*__single*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__indexable * __bidi_indexable ptrArrayAPtrArrayPtrBound2)[1] = (int *__indexable(*__indexable * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__indexable(*__indexable * __bidi_indexable implicitPtrArrayAPtrArrayPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + + // passing + + // expected-error@+1{{int *__indexable*__single' to parameter of incompatible nested pointer type 'int **'}} + foo(ptrArrayPtrThin); + foo(ptrThinPtrBound); // ok + foo(ptrThinPtrThin); // ok + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + fooBound(ptrThin); + fooBound(*ptrBoundPtrThin); // ok + + // expected-error@+1{{passing 'int *__indexable(*__single)[1]' to parameter of incompatible nested pointer type 'int *__single(*__unsafe_indexable)[1]'}} + arrayFoo(ptrArrayAPtrThin); + arrayFoo(ptrThinAPtrBound); // ok + arrayFoo(ptrThinAPtrThin); // ok + + long intVal; + int *ptr = (int*)intVal; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + ptr = intVal; + + // FIXME: rdar://70651808 + // expected-error@+1{{incompatible integer to pointer conversion initializing 'int *__unsafe_indexable' with an expression of type 'long'}} + int *__unsafe_indexable ptrUnsafe = intVal; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + int *__single ptrThin2 = intVal; + + int **ptrPtr = (int**)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtr = ptr; + + int ***ptrPtrPtr = (int***)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtrPtr = ptr; + + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + bar_t fp1 = foo; + baz_t fp2 = foo; +} diff --git a/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c b/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c new file mode 100644 index 0000000000000..e8c18602e06a9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -flax-vector-conversions=none -ffreestanding -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -flax-vector-conversions=none -ffreestanding -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include +struct oq_struct; + +typedef struct oq_struct* oq_struct_t; +#define NULL ((void *__single)0) + +oq_struct_t __unsafe_indexable foo(); + +oq_struct_t test() { + oq_struct_t local = 0; // expected-note{{pointer 'local' declared here}} + // expected-error@+1{{cannot assign to indexable pointer with type 'struct oq_struct *__bidi_indexable' from __single pointer to incomplete type 'oq_struct_t __single' (aka 'struct oq_struct *__single'); consider declaring pointer 'local' as '__single'}} + local = __unsafe_forge_single(oq_struct_t, foo()); + return local; +} + +void test_sve(svint8_t *arg) { + // expected-error@+1{{cannot initialize indexable pointer with type 'svint8_t *__bidi_indexable' (aka '__SVInt8_t *__bidi_indexable') from __single pointer to incomplete type 'svint8_t *__single' (aka '__SVInt8_t *__single'); consider declaring pointer 'local' as '__single'}} + svint8_t *local = arg; // expected-note{{pointer 'local' declared here}} +} + +typedef void(func_t)(void); +// function type is okay because it will always be '__single' +void test_func(func_t *arg) { + func_t *local = arg; +} + +void test_null() { + int *impl_bidi_ptr = NULL; + int *__bidi_indexable bidi_ptr = NULL; + oq_struct_t __indexable ind_ptr = NULL; +} + +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (int *__indexable)NULL; + int *impl_bidi_ptr3 = (void *)(char *)NULL; +} + +// The extra note is omitted because another note about the param is already emitted +// and there's no FixIt. +// expected-note@+1{{passing argument to parameter 'param' here}} +void bidi_sink(void* __bidi_indexable param); + +// The extra note is omitted because another note about the param is already emitted +// and there's no FixIt. +// expected-note@+1{{passing argument to parameter here}} +void bidi_sink_no_param_name(void* __bidi_indexable); + +int* __bidi_indexable single_opaque_assigned_to_indexable_ret( + void* __single p) { + // expected-error@+1{{cannot return __single pointer to incomplete type 'void *__single' when return type is an indexable pointer 'int *__bidi_indexable'; consider making the return type '__single'}} + return p; +} + +void single_opaque_arg_passed(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'param' parameter '__single'}} + bidi_sink(p); +} + +void single_opaque_arg_passed2(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + bidi_sink_no_param_name(p); +} + +__ptrcheck_abi_assume_bidi_indexable(); + +// expected-note@+1{{passing argument to parameter 'implicit_bidi' here}} +void implicit_bidi_sink(void* implicit_bidi); + +// expected-note@+3{{pointer 'implicit_bidi2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2{{passing argument to parameter 'implicit_bidi2' here}} +// expected-note@+1{{passing argument to parameter 'implicit_bidi2' here}} +void implicit_bidi_sink2(void* implicit_bidi2); + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_bidi_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi' parameter '__single'}} + implicit_bidi_sink(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + implicit_bidi_sink2(p); + implicit_bidi_sink2(p); +} diff --git a/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c b/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c new file mode 100644 index 0000000000000..a135fa74124f7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -verify %s + +#include + +int foo(int * __indexable ptr, unsigned idx) { + ptr -= idx; + // expected-error@-1 {{decremented indexable pointer 'ptr' is out of bounds}} + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} diff --git a/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c b/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c new file mode 100644 index 0000000000000..0e187859d53bd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int array[10] = {0}; +int *__indexable idxa = array - 1; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable idxb = array; +int *__indexable idxc = array + 9; +int *__indexable idxd = array + 10; // expected-error{{initializer element is not a compile-time constant}} + +struct { + int p; + int q[10]; +} struct_with_array; +int *__indexable idxe = struct_with_array.q - 1; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable idxf = struct_with_array.q; +int *__indexable idxg = struct_with_array.q + 9; +int *__indexable idxh = struct_with_array.q + 10; // expected-error{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c b/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c new file mode 100644 index 0000000000000..283caa0edb4c9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int array[10] = {0}; + +const int * __bidi_indexable const bidx = &array[9] - 10; + +const int * __indexable const idxa = bidx; +// expected-error@-1{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/init-struct-const-count.c b/clang/test/BoundsSafety/Sema/init-struct-const-count.c new file mode 100644 index 0000000000000..e4c02ed169f0a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-struct-const-count.c @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fno-bounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=all -verify %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c b/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c new file mode 100644 index 0000000000000..3aed0d6ffa30d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -fsyntax-only -verify -Wthread-safety %s + +#include + +#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__))) +#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__))) +#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__))) + +void elf_fun_params(int lvar EXCLUSIVE_LOCK_FUNCTION()); // \ + // expected-warning {{'exclusive_lock_function' attribute only applies to functions}} +void slf_fun_params(int lvar SHARED_LOCK_FUNCTION()); // \ + // expected-warning {{'shared_lock_function' attribute only applies to functions}} +void uf_fun_params(int lvar UNLOCK_FUNCTION()); // \ + // expected-warning {{'unlock_function' attribute only applies to functions}} + +// regression tests added for rdar://92699615 +typedef bool __attribute__((capability("role"))) role_t; +extern role_t guard1; +int __attribute__((guarded_by(guard1))) alloc1; +extern int guard2; +int __attribute__((guarded_by(guard2))) alloc2; // \ + // expected-warning{{'guarded_by' attribute requires arguments whose type is annotated with 'capability' attribute; type here is 'int'}} +int __attribute__((guarded_by(guard3))) alloc3; // \ +// expected-error{{use of undeclared identifier 'guard3'}} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c new file mode 100644 index 0000000000000..48fc37a88ed4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c @@ -0,0 +1,212 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void none_none(void) { + int len1; + int *__counted_by(len1) ptr1; + + int len2; + int *__sized_by(len2) ptr2; +} + +void none_static(void) { + int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void none_auto(void) { + int len1; + auto int *__counted_by(len1) ptr1; + + int len2; + auto int *__sized_by(len2) ptr2; +} + +void none_register(void) { + int len1; + register int *__counted_by(len1) ptr1; + + int len2; + register int *__sized_by(len2) ptr2; +} + +void static_none(void) { + static int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void static_static(void) { + static int len1; + static int *__counted_by(len1) ptr1; + + static int len2; + static int *__sized_by(len2) ptr2; +} + +void static_auto(void) { + static int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void static_register(void) { + static int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void auto_none(void) { + auto int len1; + int *__counted_by(len1) ptr1; + + auto int len2; + int *__sized_by(len2) ptr2; +} + +void auto_static(void) { + auto int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + auto int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void auto_auto(void) { + auto int len1; + auto int *__counted_by(len1) ptr1; + + auto int len2; + auto int *__sized_by(len2) ptr2; +} + +void auto_register(void) { + auto int len1; + register int *__counted_by(len1) ptr1; + + auto int len2; + register int *__sized_by(len2) ptr2; +} + +void register_none(void) { + register int len1; + int *__counted_by(len1) ptr1; + + register int len2; + int *__sized_by(len2) ptr2; +} + +void register_static(void) { + register int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + register int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void register_auto(void) { + register int len1; + auto int *__counted_by(len1) ptr1; + + register int len2; + auto int *__sized_by(len2) ptr2; +} + +void register_register(void) { + register int len1; + register int *__counted_by(len1) ptr1; + + register int len2; + register int *__sized_by(len2) ptr2; +} + +void extern_none(void) { + extern int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_static(void) { + extern int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_auto(void) { + extern int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_register(void) { + extern int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_none(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_static(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_auto(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_register(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c new file mode 100644 index 0000000000000..d1bccdfd4169a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c @@ -0,0 +1,113 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +int g_len1; + +void f1(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(g_len1) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(g_len1) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(g_len1) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(g_len1) ptr4; +} + +static int g_len2; + +void f2(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(g_len2) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(g_len2) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(g_len2) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(g_len2) ptr4; +} + +int g_len3; + +void f3(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(g_len3) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(g_len3) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(g_len3) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(g_len3) ptr4; +} + +static int g_len4; + +void f4(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(g_len4) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(g_len4) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(g_len4) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(g_len4) ptr4; +} + +void f5(void) { + static int len; + { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(len) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(len) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(len) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(len) ptr4; + } +} + +void f6(void) { + int len; + { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(len) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(len) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(len) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(len) ptr4; + } +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c new file mode 100644 index 0000000000000..b731af8927251 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int glen; +extern int extglen; + +void ptr(void) { + extern int len1; + extern int *__counted_by(len1) ptr1; + + extern int len2; + extern int *__sized_by(len2) ptr2; + + int len3; + // expected-error@+3{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__counted_by(len3) ptr3; + + extern int len4; + // expected-error@+3{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(len4) ptr4; + + // expected-error@+4{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + // expected-error@+3{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(extglen) ptr5; + +} + +void fptr(void) { + extern int *__counted_by(42) (*fptr1)(void); + + extern int *__sized_by(42) (*fptr2)(void); + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__counted_by(42) (*fptr3)(void); + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(42) (*fptr4)(void); +} + +void incomplete_array(void) { + extern int array1[__counted_by(42)]; + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int array2[__counted_by(42)]; + + extern int len3; + // expected-error@+3{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int array3[__counted_by(len3)]; + +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c new file mode 100644 index 0000000000000..84bd39f425f3e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void Test() { + int len; + + if (len) { + int *__counted_by(len) ptr; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + } + // expected-error@+2 3{{local variable len2 must be declared right next to its dependent decl}} + // expected-error@+3 3{{local variable ptr2 must be declared right next to its dependent decl}} rdar://115657607 + int len2; + // expected-note@+1 4{{previous use is here}} + int *__counted_by(len2) ptr2; + int *__counted_by(len2) ptr3; // expected-error{{variable 'len2' referred to by __counted_by variable cannot be used in other dynamic bounds attributes}} + int *__counted_by_or_null(len2) ptr4; // expected-error{{variable 'len2' referred to by __counted_by_or_null variable cannot be used in other dynamic bounds attributes}} + int *__sized_by(len2) ptr5; // expected-error{{variable 'len2' referred to by __sized_by variable cannot be used in other dynamic bounds attributes}} + int *__sized_by_or_null(len2) ptr6; // expected-error{{variable 'len2' referred to by __sized_by_or_null variable cannot be used in other dynamic bounds attributes}} +} diff --git a/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c b/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c new file mode 100644 index 0000000000000..10fa3165e26b9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c @@ -0,0 +1,260 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(len) buf; + int len; +}; + +struct S_Nullable { + int *__counted_by_or_null(len) buf; + int len; +}; + +struct T { + int *__counted_by(len + 1) buf; + int len; +}; + +struct T_Nullable { + int *__counted_by_or_null(len + 1) buf; + int len; +}; + +struct U { + int *__counted_by(len) buf; + int *__counted_by(len) buf2; + int len; +}; + +struct U_Nullable { + int *__counted_by_or_null(len) buf; + int *__counted_by_or_null(len) buf2; + int len; +}; + +struct V { + int *__sized_by(len) buf; + int *__counted_by(len) buf2; + int *__indexable buf3; + int *__bidi_indexable buf4; + int *__single buf5; + int *__sized_by_or_null(len) buf6; + int *__counted_by_or_null(len) buf7; + + int len; +}; + +int arr[10]; + +void foo(int *out_len, int *__counted_by(*out_len) * out_buf) { + // expected-note@-1 4{{passing argument to parameter 'out_buf' here}} + *out_len = 9; + *out_buf = arr; + return; +} + +void foo_nullable(int *out_len, int *__counted_by_or_null(*out_len) * out_buf) { + // expected-note@-1 4{{passing argument to parameter 'out_buf' here}} + *out_len = 9; + *out_buf = arr; + return; +} + +void bar(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void bar_nullable(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void baz123(int *__counted_by(*out_len) buf, int *out_len, int *fake_out_len); +void baz132(int *__counted_by(*out_len) buf, int *fake_out_len, int *out_len); +void baz213(int *out_len, int *__counted_by(*out_len) buf, int *fake_out_len); +void baz312(int *fake_out_len, int *__counted_by(*out_len) buf, int *out_len); +void bazo123(int *__counted_by(*out_len) *out_buf, int *out_len, int *fake_out_len); +void bazo132(int *__counted_by(*out_len) *out_buf, int *fake_out_len, int *out_len); +void bazo213(int *out_len, int *__counted_by(*out_len) *out_buf, int *fake_out_len); +void bazo312(int *fake_out_len, int *__counted_by(*out_len) *out_buf, int *out_len); + +void baz123_nullable(int *__counted_by_or_null(*out_len) buf, int *out_len, int *fake_out_len); +void baz132_nullable(int *__counted_by_or_null(*out_len) buf, int *fake_out_len, int *out_len); +void baz213_nullable(int *out_len, int *__counted_by_or_null(*out_len) buf, int *fake_out_len); +void baz312_nullable(int *fake_out_len, int *__counted_by_or_null(*out_len) buf, int *out_len); +void bazo123_nullable(int *__counted_by_or_null(*out_len) *out_buf, int *out_len, int *fake_out_len); +void bazo132_nullable(int *__counted_by_or_null(*out_len) *out_buf, int *fake_out_len, int *out_len); +void bazo213_nullable(int *out_len, int *__counted_by_or_null(*out_len) *out_buf, int *fake_out_len); +void bazo312_nullable(int *fake_out_len, int *__counted_by_or_null(*out_len) *out_buf, int *out_len); + +int main() { + struct S s = {0}; + struct S_Nullable s_n = {0}; + // expected-error@+1{{initializing 't.buf' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct T t = {0}; + struct T_Nullable t_n = {0}; + struct U u = {0}; + struct U_Nullable u_n = {0}; + struct V v = {0}; + + int local_len = 10; + int *__single ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + *ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &s_n.len; // expected-error{{field referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_len = &struct_ptr->len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &(*struct_ptr).len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_len = 100; + + foo(&s.len, &s.buf); + foo(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t.len, &t.buf); // expected-error{{incompatible count expression (*out_len) vs. (len + 1) in argument to function}} + foo(&u.len, &u.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + bar(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + bar(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + bar(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + foo(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + + foo(&s_n.len, &s_n.buf); + foo(&local_len, &s_n.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t_n.len, &t_n.buf); // expected-error{{incompatible count expression (*out_len) vs. (len + 1) in argument to function}} + foo(&u_n.len, &u_n.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len)' (aka 'int *__single'), refers to 'len'}} + bar(&s_n.len, &s_n.buf); // expected-error{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + foo_nullable(&s.len, &s.buf); + foo_nullable(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo_nullable(&t.len, &t.buf); // expected-error{{incompatible count expression (*out_len) vs. (len + 1) in argument to function}} + foo_nullable(&u.len, &u.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo_nullable(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo_nullable(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + foo_nullable(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + bar_nullable(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + foo_nullable(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + + foo_nullable(&s_n.len, &s_n.buf); + foo_nullable(&local_len, &s_n.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo_nullable(&t_n.len, &t_n.buf); // expected-error{{incompatible count expression (*out_len) vs. (len + 1) in argument to function}} + foo_nullable(&u_n.len, &u_n.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&s_n.len, &s_n.buf); // expected-error{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + baz123(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz132(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz213(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz312(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + baz123(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz132(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz213(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz312(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + bazo123(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo132(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo213(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo312(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + bazo123(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo132(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo213(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo312(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + baz123_nullable(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz132_nullable(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz213_nullable(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz312_nullable(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + bazo123_nullable(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo132_nullable(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo213_nullable(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo312_nullable(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + baz123_nullable(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz132_nullable(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz213_nullable(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz312_nullable(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + bazo123_nullable(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo132_nullable(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo213_nullable(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo312_nullable(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/missing-fam-name.c b/clang/test/BoundsSafety/Sema/missing-fam-name.c new file mode 100644 index 0000000000000..c603d68774c69 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/missing-fam-name.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct type { + int count; + char [__counted_by(count)]; // expected-error{{expected member name or ';' after declaration specifiers}} +}; diff --git a/clang/test/BoundsSafety/Sema/mock-header.h b/clang/test/BoundsSafety/Sema/mock-header.h new file mode 100644 index 0000000000000..b242addddf3c6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-header.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef SYSTEM_HEADER +#pragma clang system_header +#endif + +int *returns_pointer(void); diff --git a/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h b/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h new file mode 100644 index 0000000000000..1680868eda97c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h @@ -0,0 +1 @@ +void foo(int array[]); diff --git a/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h b/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h new file mode 100644 index 0000000000000..c93491f808138 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h @@ -0,0 +1,10 @@ +#ifndef __EXTERN_ARRAY_MOCK_STD_H__ +#define __EXTERN_ARRAY_MOCK_STD_H__ +extern unsigned externArray[]; + +inline int f() { + int *ptr = externArray; + return *ptr; +} + +#endif diff --git a/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c b/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c new file mode 100644 index 0000000000000..1723d879fb3b4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +struct Foo { + int *__bidi_indexable *__single ptrBoundPtrThin; + // CHECK: FieldDecl {{.+}} ptrBoundPtrThin 'int *__bidi_indexable*__single' +}; + + +typedef struct Foo Foo; + +Foo *__bidi_indexable *__single Test (Foo *__single *__bidi_indexable argFooPtrThinPtrBound) { + Foo *__single *__bidi_indexable localFooPtrThinPtrBound = argFooPtrThinPtrBound; + Foo *__bidi_indexable *__single Res; + return Res; +} +// CHECK: FunctionDecl {{.+}} Test 'Foo *__bidi_indexable*__single(Foo *__single*__bidi_indexable)' +// CHECK: VarDecl {{.+}} localFooPtrThinPtrBound 'Foo *__single*__bidi_indexable' cinit +// CHECK: VarDecl {{.+}} Res 'Foo *__bidi_indexable*__single' diff --git a/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c b/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c new file mode 100644 index 0000000000000..b1c0dfda97879 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; + int *__counted_by(cnt2) ptr2; +}; + +void full_group_assigned(void) { + int arr[10]; + struct T t; + + t.cnt1 = 1; + t.ptr = arr; + t.ptr2 = arr; + t.cnt2 = 2; +} + +void missing_cnt1(void) { + int arr[10]; + struct T t; + + t.ptr = arr; // expected-error{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt1'; add self assignment 't.cnt1 = t.cnt1' if the value has not changed}} + t.ptr2 = arr; + t.cnt2 = 2; +} + +void missing_cnt2(void) { + int arr[10]; + struct T t; + + t.ptr = arr; // expected-error{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.cnt1 = 5; + t.ptr2 = arr; +} + +void missing_ptr(void) { + int arr[10]; + struct T t; + + t.cnt1 = 5; // expected-error{{assignment to 't.cnt1' requires corresponding assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr'; add self assignment 't.ptr = t.ptr' if the value has not changed}} + t.cnt2 = 0; + t.ptr2 = arr; +} + + +void missing_ptr2(void) { + int arr[10]; + struct T t; + + t.ptr = &arr[0]; + t.cnt1 = 5; + t.cnt2 = 0; // expected-error{{assignment to 't.cnt2' requires corresponding assignment to 'int *__single __counted_by(cnt2)' (aka 'int *__single') 't.ptr2'; add self assignment 't.ptr2 = t.ptr2' if the value has not changed}} +} + +void only_cnt1(void) { + int arr[10]; + struct T t; + + // expected-error@+1{{assignment to 't.cnt1' requires corresponding assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr'; add self assignment 't.ptr = t.ptr' if the value has not changed}} + t.cnt1 = 5; +} + +void only_ptr(void) { + int arr[10]; + struct T t; + // expected-error@+2{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt1'; add self assignment 't.cnt1 = t.cnt1' if the value has not changed}} + // expected-error@+1{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.ptr = arr; +} + +void only_ptr2(void) { + int arr[10]; + struct T t; + // expected-error@+1{{assignment to 'int *__single __counted_by(cnt2)' (aka 'int *__single') 't.ptr2' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.ptr2 = arr; +} + +void self_assign_ptr_cnt2(void) { + int arr[10]; + struct T t; + + t.ptr = t.ptr; + t.ptr2 = arr; + t.cnt2 = t.cnt2; + t.cnt1 = 3; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/negative-count.c b/clang/test/BoundsSafety/Sema/negative-count.c new file mode 100644 index 0000000000000..60e61f85800b3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/negative-count.c @@ -0,0 +1,125 @@ + +// RUN: %clang_cc1 -fbounds-safety -triple arm64 -verify %s + +#include +#include + +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_literal_count(int * arg) { + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(-1) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len) p = arg; + return p; +} + +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} + +void foo(int *__counted_by(len) p, int len); + +void to_counted_by_arg(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + foo(p, len); + int len2 = -1; + // expected-error@+1{{possibly initializing 'p2' of type 'int *__single __counted_by_or_null(len2)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len2) p2 = arg; + foo(p2, len2); + // expected-error@+1{{negative count value of -1 for 'p' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(NULL, -1); + foo(NULL, 0); + // expected-error@+1{{negative count value of -1 for 'p' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(arg, -1); + foo(arg, 0); +} + +void bar(int *__counted_by_or_null(len) p, int len); + +void to_counted_by_or_null_arg(int * arg) { + // expected-error@+1{{possibly passing non-null to parameter 'p' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + bar(arg, -1); + bar(arg, 0); + bar(NULL, -1); + bar(NULL, 0); +} + +int * __counted_by_or_null(len) nullable_ret(int len); +int * __counted_by(len) nonnullable_ret(int len); + +void ret_values() { + int len = -1; + nullable_ret(-1); + nullable_ret(len); + nonnullable_ret(-1); + nonnullable_ret(len); + len = 0; + nullable_ret(len); + nonnullable_ret(len); +} + +struct offset_fam { + int len; + // expected-note@+1 5{{initialized flexible array member 'buf' is here}} + int buf[__counted_by(len-1)]; +}; + +struct offset_nonnullable { + int len; + int * __counted_by(len-1) buf; +}; + +struct offset_nullable { + int len; + int * __counted_by_or_null(len-1) buf; +}; + +void struct_fields(int * arg) { + struct offset_fam fam1; // rdar://127523062 We should not allow stack allocated flexible array members + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} + struct offset_fam fam2 = {}; + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} + struct offset_fam fam3 = { .buf = {} }; + // expected-error@+1{{flexible array member is initialized with 1 element, but count value is initialized to -1}} + static struct offset_fam fam4 = { .buf = {1} }; + static struct offset_fam fam5 = { .buf = {}, .len = 1}; + static struct offset_fam fam6 = { .len = 1, .buf = {} }; + // expected-error@+1{{flexible array member is initialized with 1 element, but count value is initialized to 0}} + static struct offset_fam fam7 = { .len = 1, .buf = {1} }; + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to 1}} + static struct offset_fam fam8 = { .len = 2, .buf = {} }; + static struct offset_fam fam9 = { .len = 2, .buf = {1} }; + + // expected-error@+1{{negative count value of -1 for 'nn1.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn1; + // expected-error@+1{{negative count value of -1 for 'nn2.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn2 = {}; + // expected-error@+1{{negative count value of -1 for 'nn3.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn3 = { .buf = arg }; + struct offset_nonnullable nn4 = { .len = 1, .buf = arg }; + struct offset_nonnullable nn5 = { .len = 2, .buf = arg }; + + struct offset_nullable n1; + struct offset_nullable n2 = {}; + // expected-error@+1{{possibly initializing 'n3.buf' of type 'int *__single __counted_by_or_null(len - 1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n3 = { .buf = arg }; + struct offset_nullable n4 = { .len = 1, .buf = arg }; + struct offset_nullable n5 = { .len = 2, .buf = arg }; +} diff --git a/clang/test/BoundsSafety/Sema/negative-size.c b/clang/test/BoundsSafety/Sema/negative-size.c new file mode 100644 index 0000000000000..f26c538dc7d16 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/negative-size.c @@ -0,0 +1,109 @@ + +// RUN: %clang_cc1 -fbounds-safety -triple arm64 -verify %s + +#include +#include + +int *__bidi_indexable to_bidi(int * arg) { + int size = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(size)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_literal_size(int * arg) { + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(-1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(-1) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_const_size(int * arg) { + const int size = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(-1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size) p = arg; + return p; +} + +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int size = -1; + int * __sized_by_or_null(size) p = NULL; + arg = p; + p = arg; + size = size; + arg = p; +} + +void foo(int *__sized_by(size) p, int size); + +void to_sized_by_arg(int * arg) { + int size = -1; + int * __sized_by_or_null(size) p = NULL; + foo(p, size); + int size2 = -1; + // expected-error@+1{{possibly initializing 'p2' of type 'int *__single __sized_by_or_null(size2)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size2) p2 = arg; + foo(p2, size2); + // expected-error@+1{{negative size value of -1 for 'p' of type 'int *__single __sized_by(size)' (aka 'int *__single')}} + foo(NULL, -1); + foo(NULL, 0); + // expected-error@+1{{negative size value of -1 for 'p' of type 'int *__single __sized_by(size)' (aka 'int *__single')}} + foo(arg, -1); + foo(arg, 0); +} + +void bar(int *__sized_by_or_null(size) p, int size); + +void to_sized_by_or_null_arg(int * arg) { + // expected-error@+1{{possibly passing non-null to parameter 'p' of type 'int *__single __sized_by_or_null(size)' (aka 'int *__single') with size value of -1; explicitly pass null to remove this warning}} + bar(arg, -1); + bar(arg, 0); + bar(NULL, -1); + bar(NULL, 0); +} + +int * __sized_by_or_null(size) nullable_ret(int size); +int * __sized_by(size) nonnullable_ret(int size); + +void ret_values() { + int size = -1; + nullable_ret(-1); + nullable_ret(size); + nonnullable_ret(-1); + nonnullable_ret(size); + size = 0; + nullable_ret(size); + nonnullable_ret(size); +} + +struct offset_nonnullable { + int size; + int * __sized_by(size-1) buf; +}; + +struct offset_nullable { + int size; + int * __sized_by_or_null(size-1) buf; +}; + +void struct_fields(int * arg) { + // expected-error@+1{{negative size value of -1 for 'nn1.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn1; + // expected-error@+1{{negative size value of -1 for 'nn2.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn2 = {}; + // expected-error@+1{{negative size value of -1 for 'nn3.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn3 = { .buf = arg }; + struct offset_nonnullable nn4 = { .size = 1, .buf = arg }; + struct offset_nonnullable nn5 = { .size = 5, .buf = arg }; + // expected-error@+1{{negative size value of -1 for 'nn6.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn6 = { .buf = arg, .size = 0 }; + + struct offset_nullable n1; + struct offset_nullable n2 = {}; + // expected-error@+1{{possibly initializing 'n3.buf' of type 'int *__single __sized_by_or_null(size - 1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n3 = { .buf = arg }; + struct offset_nullable n4 = { .size = 1, .buf = arg }; + struct offset_nullable n5 = { .size = 5, .buf = arg }; + // expected-error@+1{{possibly initializing 'n6.buf' of type 'int *__single __sized_by_or_null(size - 1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n6 = { .buf = arg, .size = 0 }; +} + diff --git a/clang/test/BoundsSafety/Sema/nested-anon-union-count.c b/clang/test/BoundsSafety/Sema/nested-anon-union-count.c new file mode 100644 index 0000000000000..a4f9e685755da --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-anon-union-count.c @@ -0,0 +1,17 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Extracted from nested-struct-member-count.c because the error message refers to a line number, making it fragile +struct InnerAnonUnion { + struct B { + union { + int len; + float f; + }; + int dummy; + } hdr; + char fam[__counted_by(hdr.len)]; // expected-error-re{{count parameter refers to union 'hdr.' of type 'union B::(anonymous at {{.*}}/BoundsSafety/Sema/nested-anon-union-count.c:10:9)'}} +}; diff --git a/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c b/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c new file mode 100644 index 0000000000000..68675adf71f85 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct nested { + int *__counted_by(l) bp; + int *bp2; + int l; +}; + +struct S { + int l; + struct nested n1; + int *__counted_by(l) ptr; + struct nested n2; +}; + +int main() { + struct S s; + struct S *sp = &s; + + int arr[10]; + sp->n1.bp = arr; + sp->n1.l = 9; + + sp->n2.l = 10; // expected-error{{assignment to 'sp->n2.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n2.bp'; add self assignment 'sp->n2.bp = sp->n2.bp' if the value has not changed}} + sp->n1.l = 0; // expected-error{{assignment to 'sp->n1.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n1.bp'; add self assignment 'sp->n1.bp = sp->n1.bp' if the value has not changed}} + sp->n2.bp = &arr[1]; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n2.bp' requires corresponding assignment to 'sp->n2.l'; add self assignment 'sp->n2.l = sp->n2.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-dependent-count.c b/clang/test/BoundsSafety/Sema/nested-dependent-count.c new file mode 100644 index 0000000000000..69a2b0bfda598 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-dependent-count.c @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(len) *__counted_by(len) buf; // expected-error{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *len; +}; + +int fooOut(int *__counted_by(len) *out_buf, int len); // ok +int fooOutOut(int *__counted_by(*len) *out_buf, int *len); // ok +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int bar(int *__counted_by(len) *__counted_by(len) out_buf, int len); + +int baz() { + int len; + int *__counted_by(len) buf; + + // expected-error@+1{{passing address of 'buf' as an indirect parameter; must also pass 'len' or its address because the type of 'buf', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + fooOut(&buf, len); + fooOutOut(&buf, &len); + + int *p = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + return 0; +} + +int main() { + int len; + + { + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(len) *nested_buf; + } + + len = 100; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-endedby.c b/clang/test/BoundsSafety/Sema/nested-endedby.c new file mode 100644 index 0000000000000..5a4321be16607 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-endedby.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +struct S { + unsigned len; + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end) *__counted_by(len) buf; + int **end; + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end2) *__ended_by(end3) buf2; + int **end2; + int *end3; +}; + +int fooOut(int *__ended_by(end) *out_start, int *end); // ok +int fooOutOut(int *__ended_by(*out_end) *out_buf, int **out_end); // ok +// FIXME: doesn't sound exactly right +// expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} +int bar(int *__ended_by(end1) *__ended_by(end2) out_buf, int **end1, int *end2); + +void baz() { + int *end; + int *__ended_by(end) start; + + fooOut(&start, end); + fooOutOut(&start, &end); + + int **p = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable}} + // expected-error@+1{{pointer with '__ended_by' cannot be pointed to by any other variable}} + p = &start; +} + +void qux() { + int *end; + + { + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end) *nested_buf; + // expected-error@+2{{local variable buf must be declared right next to its dependent decl}} + // expected-error@+1{{argument of '__ended_by' attribute cannot refer to declaration from a different scope}} + int *__ended_by(end) buf; + } + + end = 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-struct-member-count.c b/clang/test/BoundsSafety/Sema/nested-struct-member-count.c new file mode 100644 index 0000000000000..8f9450513c422 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-struct-member-count.c @@ -0,0 +1,449 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void side_effect(); + +void ext(int *p); + +// this pointer can never be created in safe -fbounds-safety code since it requires taking the pointer of an array with __counted_by +// expected-error@+1{{pointer to incomplete __counted_by array type 'int[]' not allowed; did you mean to use a nested pointer type?}} +void ext2(int (*ptr)[__counted_by(*len)], int *len) { + *len = 2; // expected-error{{assignment to '*len' requires corresponding assignment to 'int[__counted_by(*len)]' (aka 'int[]') '*ptr'; add self assignment '*ptr = *ptr' if the value has not changed}} +} + +void ext3(char *__counted_by(*len) ptr, int *len); +void ext4(void *p); + +struct SimpleInner { + int dummy; + int len; +}; + +void ext_set_len(struct SimpleInner * p) { + p->len = 2; // len is not referred to by any counted_by attributes yet +} + +struct SimpleOuter { + struct SimpleInner hdr; + char fam[__counted_by(hdr.len)]; // expected-note 17{{referred to by count parameter here}} +}; + +void set_len(struct SimpleInner * p) { + p->len = 2; // struct SimpleInner doesn't contain any FAMs, so not checked + // we cannot take the address of SimpleOuter::hdr though, so this is never referred to by a FAM + + side_effect(); + + p->len++; +} + +void set_len2(struct SimpleOuter * s) { + s->hdr.len = 2; // expected-error{{assignment to 's->hdr.len' requires an immediately preceding assignment to 's' with a wide pointer}} + + side_effect(); + + s->hdr.len++; // expected-error{{assignment to 's->hdr.len' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +struct SimpleInner2 { // avoid sharing with previous test cases + int dummy; + int len; +}; +struct SimpleOuter2 { + struct SimpleInner2 hdr; + char fam[__counted_by(hdr.len)]; +}; +struct UnrelatedFAMOuter { + struct SimpleInner hdr; + int dummy[]; +}; +struct UnrelatedCountedByFAMOuter { + struct SimpleInner hdr; + int outer_len; + char fam[__counted_by(outer_len)]; +}; + +// regression tests for assignment to count in struct with FAM that doesn't refer to the count (but some other struct does) +void set_len3(struct UnrelatedFAMOuter * s) { + s->hdr.len = 1; +} +void set_len4() { + struct UnrelatedFAMOuter s; + s.hdr.len = 1; +} +void set_len5(struct UnrelatedCountedByFAMOuter * s) { + s->hdr.len = 1; +} +void set_len6() { + struct UnrelatedCountedByFAMOuter s; + s.hdr.len = 1; +} + +void address_of_nested_len(struct SimpleInner * p) { + (void)&p->len; + int * p2 = &p->len; // ok, base struct contains no FAM +} + +void address_of_len(struct SimpleOuter * s) { + int * p = &s->hdr.len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p2 = &(s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p3 = &(s->hdr).len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext(&s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext2(&s->fam, &s->hdr.len); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-error@-1{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-note@-2{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(hdr.len)]'}} + + ext2((int (*__single)[__counted_by(*len)]) &s->fam, &s->hdr.len); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(hdr.len)]'}} + // expected-error@-2{{cannot take address of field referred to by __counted_by on a flexible array member}} + + // type confusion + ext2(s->fam, &s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext3(s->fam, &s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4(&s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + (void) &s->hdr.len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +void address_of_inner_struct(struct SimpleOuter * s) { + (void) &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4((void*)&s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + // these are valid casts in plain C, but result in unsafe assignment to s->hdr.len due to type confusion in the -fbounds-safety type system + set_len((struct SimpleInner *)s); + set_len((struct SimpleInner *)&s->hdr.dummy); +} + +void assign_inner_struct(struct SimpleOuter * s, struct SimpleInner * i) { + struct SimpleInner * p = &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + s->hdr = (struct SimpleInner) {0, 10}; // expected-error{{cannot assign 's->hdr' because it contains field 'len' referred to by flexible array member 'fam'}} + + *&s->hdr = *i; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +struct InnerFAM { + int len; + int inner_fam[__counted_by(len)]; // expected-note{{previous use is here}} +}; + +void ext_set_inner_len(struct InnerFAM * p) { + p = p; + p->len = 2; +} + +struct MultipleFAMs { + // rdar://132712477 hdr.len is not bounds checked with respect to hdr.inner_fam, this should be an error + struct InnerFAM hdr; // expected-warning{{field 'hdr' with variable sized type 'struct InnerFAM' not at the end of a struct or class is a GNU extension}} + int offset; + char fam[__counted_by(hdr.len - offset)]; // expected-error{{field 'len' referred to by flexible array member cannot be used in other dynamic bounds attributes}} +}; + +void set_inner_len(struct InnerFAM * p) { + p = p; + p->len = 2; +} + +void set_len_via_fam(struct MultipleFAMs * s) { + s->hdr.inner_fam[0] = 2; // unsafe write to s->offset + s->hdr.inner_fam[1] = 2; +} + +struct InnerInner { + int dummy; + int innermost_len; +}; +struct MediumInner { + struct InnerInner next; + int dummy; +}; +struct DeepNestingOuter { + struct MediumInner hdr; + int outer_len; + char fam[__counted_by(outer_len + hdr.next.innermost_len)]; // expected-note 10{{referred to by count parameter here}} +}; + +void addresses_with_deep_nesting(struct DeepNestingOuter * s) { + int * p = &s->outer_len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + struct MediumInner * p2 = &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + struct InnerInner * p3 = &s->hdr.next; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p4 = &s->hdr.next.innermost_len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4(&s->outer_len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr.next); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr.next.innermost_len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +void assign_with_deep_nesting(struct DeepNestingOuter * s, int a, struct MediumInner b, struct InnerInner c) { + s->outer_len = a; // expected-error{{assignment to 's->outer_len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->hdr = b; // expected-error{{cannot assign 's->hdr' because it contains field 'next' referred to by flexible array member 'fam'}} + s->hdr.next = c; // expected-error{{cannot assign 's->hdr.next' because it contains field 'innermost_len' referred to by flexible array member 'fam'}} + s->hdr.next.innermost_len = a; + + side_effect(); + + s = s; + s->outer_len = a; // expected-error{{assignment to 'int' 's->outer_len' requires corresponding assignment to 's->hdr.next.innermost_len'; add self assignment 's->hdr.next.innermost_len = s->hdr.next.innermost_len' if the value has not changed}} + + side_effect(); + + s = s; + s->outer_len = a; + s->hdr.next.innermost_len = a; +} + +struct InnerDup { + int dummy; + int len; +}; +struct OuterDup { + struct InnerDup a; + struct InnerDup b; + char fam[__counted_by(a.len)]; // expected-error{{cannot depend on nested field 'len' because it exists in multiple instances in struct 'OuterDup'}} +}; + +void non_aliasing_assignment_to_same_decl(struct OuterDup * s) { + s->b.len = 2; // should not affect fam + + side_effect(); + + s = s; + s->a.len = 2; // should not require assignment to s->b.len +} + +struct OuterDup2 { + struct InnerDup a; + struct InnerDup b; + char fam[__counted_by(a.len + b.len)]; // expected-error{{cannot depend on nested field 'len' because it exists in multiple instances in struct 'OuterDup2'}} +}; + +void non_aliasing_ref_to_same_decl(struct OuterDup2 * s) { + s = s; + s->a.len = 2; // should require assignment to s->b.len if OuterDup2::fam is error free + + side_effect(); + + s = s; + s->b.len = 2; // should require assignment to s->a.len if OuterDup2::fam is error free + + side_effect(); + + s = s; + s->a.len = 2; + s->b.len = 2; +} + +struct CommonHeader { + int dummy; + int len; +}; +struct SubType1 { + struct CommonHeader hdr; + char fam1[__counted_by(hdr.len)]; // expected-note{{referred to by count parameter here}} +}; +struct SubType2 { + struct CommonHeader hdr; + int offset; + char fam2[__counted_by(hdr.len - offset)]; // expected-note{{referred to by count parameter here}} +}; + +void shared_header_type(struct SubType1 * s1, struct SubType2 * s2) { + s1 = s1; + s1->hdr.len = 2; + + side_effect(); + + s1->hdr.len = 2; // expected-error{{assignment to 's1->hdr.len' requires an immediately preceding assignment to 's1' with a wide pointer}} + + side_effect(); + + s2 = s2; + s2->hdr.len = 2; + s2->offset = 1; + + side_effect(); + + s2 = s2; + s2->hdr.len = 2; // expected-error{{assignment to 'int' 's2->hdr.len' requires corresponding assignment to 's2->offset'; add self assignment 's2->offset = s2->offset' if the value has not changed}} + + side_effect(); + + s2 = s2; + s2->offset = 1; // expected-error{{assignment to 'int' 's2->offset' requires corresponding assignment to 's2->hdr.len'; add self assignment 's2->hdr.len = s2->hdr.len' if the value has not changed}} + + side_effect(); + + s1->hdr = s2->hdr; // expected-error{{cannot assign 's1->hdr' because it contains field 'len' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr = s1->hdr; // expected-error{{cannot assign 's2->hdr' because it contains field 'len' referred to by flexible array member 'fam2'}} +} + +struct CommonOuterHeader { + struct CommonHeader next; + int dummy; +}; +struct DeepSubType1 { + struct CommonOuterHeader hdr; + char fam1[__counted_by(hdr.next.len)]; // expected-note 2{{referred to by count parameter here}} +}; +struct DeepSubType2 { + struct CommonOuterHeader hdr; + int offset; + char fam2[__counted_by(hdr.next.len - offset)]; // expected-note 2{{referred to by count parameter here}} +}; + +void shared_nested_header_type(struct DeepSubType1 * s1, struct DeepSubType2 * s2) { + s1 = s1; + s1->hdr.next.len = 2; + + side_effect(); + + s1->hdr.next.len = 2; // expected-error{{assignment to 's1->hdr.next.len' requires an immediately preceding assignment to 's1' with a wide pointer}} + + side_effect(); + + s2 = s2; + s2->hdr.next.len = 2; + s2->offset = 1; + + side_effect(); + + s2 = s2; + s2->hdr.next.len = 2; // expected-error{{assignment to 'int' 's2->hdr.next.len' requires corresponding assignment to 's2->offset'; add self assignment 's2->offset = s2->offset' if the value has not changed}} + + side_effect(); + + s2 = s2; + s2->offset = 1; // expected-error{{assignment to 'int' 's2->offset' requires corresponding assignment to 's2->hdr.next.len'; add self assignment 's2->hdr.next.len = s2->hdr.next.len' if the value has not changed}} + + side_effect(); + + s1->hdr = s2->hdr; // expected-error{{cannot assign 's1->hdr' because it contains field 'next' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr = s1->hdr; // expected-error{{cannot assign 's2->hdr' because it contains field 'next' referred to by flexible array member 'fam2'}} + + side_effect(); + + s1->hdr.next = s2->hdr.next; // expected-error{{cannot assign 's1->hdr.next' because it contains field 'len' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr.next = s1->hdr.next; // expected-error{{cannot assign 's2->hdr.next' because it contains field 'len' referred to by flexible array member 'fam2'}} +}; + +struct PointerHeader { + int dummy; + int len; +}; +struct CountPointer { + struct PointerHeader hdr; + char * __counted_by(hdr.len) p; // expected-error{{invalid argument expression to bounds attribute}} + // expected-note@-1{{nested struct member in count parameter only supported for flexible array members}} +}; +struct CountPointer2 { + struct PointerHeader hdr; + char * __counted_by(hdr.len) p2; // expected-error{{invalid argument expression to bounds attribute}} + // expected-note@-1{{nested struct member in count parameter only supported for flexible array members}} +}; + +void nested_count_pointer_count(struct CountPointer * s) { + s->hdr.len = 2; +} + +struct InnerPointer { + int len; + char * __sized_by(len) p; +}; +struct OuterWithInnerPointer { + struct InnerPointer hdr; + char fam[__counted_by(hdr.len)]; +}; + +void fam_with_inner_pointer(struct OuterWithInnerPointer * s) { + s = s; + s->hdr.len = 2; // expected-error{{assignment to 's->hdr.len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 's->hdr.p'; add self assignment 's->hdr.p = s->hdr.p' if the value has not changed}} +} + +struct PointeeHeader { + int dummy; + int len; +}; +struct FAMArrow { + struct PointeeHeader *hdr; + char fam[__counted_by(hdr->len)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} +}; +struct FAMDerefStruct { + struct PointeeHeader *hdr; + char fam[__counted_by((*hdr).len)]; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} +}; + +typedef union { + int i; + float f; +} U; +struct UnionCount { + U len; + char fam[__counted_by(len.i)]; // expected-error{{count parameter refers to union 'len' of type 'U'}} + // expected-note@-1 2{{referred to by count parameter here}} +}; +void union_assign(struct UnionCount * s) { + s->len.i = 1; +} +void type_pun_assign(struct UnionCount * s) { + s->len.f = 1.0; +} +void ext_u(U * u) { + u->i = 1; +} +void union_address_param(struct UnionCount * s) { + ext_u(&s->len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} +void union_address_assign(struct UnionCount * s) { + U * u = &s->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + u->i = 1; +} +void ext_f(float * f) { + *f = 1.0; +} +void union_member_address_param(struct UnionCount * s) { + ext_f(&s->len.f); +} + +struct AnonStruct { + struct { + int len; + int dummy; + }; + char fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} rdar://125394428 +}; +struct AnonUnion { + union { + int len; + float f; + }; + char fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; +struct InnerAnonStruct { + struct A { + struct { + int len; + int dummy2; + }; + int dummy; + } hdr; + char fam[__counted_by(hdr.len)]; +}; diff --git a/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c b/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c new file mode 100644 index 0000000000000..8abb88dfeb735 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void side_effect(); + +void Test() { + int len; // // expected-error{{local variable len must be declared right next to its dependent decl}} + side_effect(); + int *__counted_by(len) ptr; // expected-error{{local variable ptr must be declared right next to its dependent decl}} +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c new file mode 100644 index 0000000000000..fef525b61ac05 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c @@ -0,0 +1,78 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +const int * auto_global_var1; +// CHECK: auto_global_var1 'const int *__single' + +const int * _Nullable auto_global_var2; +// CHECK: auto_global_var2 'const int *__single _Nullable':'const int *__single' + +const int * __ptrauth(2, 0, 0) auto_global_var3; +// CHECK: auto_global_var3 'const int *__single__ptrauth(2,0,0)' + +const int * __single global_var1; +// CHECK: global_var1 'const int *__single' + +const int * _Nullable __single global_var2; +// CHECK: global_var2 'const int *__single _Nullable':'const int *__single' + +const int * __ptrauth(2, 0, 0) __single global_var3; +// CHECK: global_var3 'const int *__single__ptrauth(2,0,0)' + + +const int * _Nullable __ptrauth(2, 0, 0) __single c1_global_var1; +// CHECK: c1_global_var1 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + +const int * _Nullable __single __ptrauth(2, 0, 0) c1_global_var2; +// CHECK: c1_global_var2 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + +const int * __ptrauth(2, 0, 0) _Nullable __single c1_global_var3; +// CHECK: c1_global_var3 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __ptrauth(2, 0, 0) __single _Nullable c1_global_var4; +// CHECK: c1_global_var4 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __single __ptrauth(2, 0, 0) _Nullable c1_global_var5; +// CHECK: c1_global_var5 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __single _Nullable __ptrauth(2, 0, 0) c1_global_var6; +// CHECK: c1_global_var6 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + + +int * const _Nullable __ptrauth(2, 0, 0) __single c2_global_var1; +// CHECK: c2_global_var1 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + +int * const _Nullable __single __ptrauth(2, 0, 0) c2_global_var2; +// CHECK: c2_global_var2 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + +int * const __ptrauth(2, 0, 0) _Nullable __single c2_global_var3; +// CHECK: c2_global_var3 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __ptrauth(2, 0, 0) __single _Nullable c2_global_var4; +// CHECK: c2_global_var4 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __single __ptrauth(2, 0, 0) _Nullable c2_global_var5; +// CHECK: c2_global_var5 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __single _Nullable __ptrauth(2, 0, 0) c2_global_var6; +// CHECK: c2_global_var6 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + + +const int * const _Nullable __ptrauth(2, 0, 0) __single c12_global_var1; +// CHECK: c12_global_var1 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' +const int * const _Nullable __single __ptrauth(2, 0, 0) c12_global_var2; +// CHECK: c12_global_var2 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __ptrauth(2, 0, 0) _Nullable __single c12_global_var3; +// CHECK: c12_global_var3 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __ptrauth(2, 0, 0) __single _Nullable c12_global_var4; +// CHECK: c12_global_var4 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __single __ptrauth(2, 0, 0) _Nullable c12_global_var5; +// CHECK: c12_global_var5 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __single _Nullable __ptrauth(2, 0, 0) c12_global_var6; +// CHECK: c12_global_var6 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c new file mode 100644 index 0000000000000..daee3393edf7e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c @@ -0,0 +1,48 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +int * _Nullable __single global_var1; +// CHECK: global_var1 'int *__single _Nullable':'int *__single' + +int * __single _Nullable global_var2; +// CHECK: global_var2 'int *__single _Nullable':'int *__single' + +void f(void) { + int * _Nullable __bidi_indexable local_var1; + // CHECK: local_var1 'int *__bidi_indexable _Nullable':'int *__bidi_indexable' + + int * __bidi_indexable _Nullable local_var2; + // CHECK: local_var2 'int *__bidi_indexable _Nullable':'int *__bidi_indexable' + + int (* _Nullable __bidi_indexable local_var3)[4]; + + int (* __bidi_indexable _Nullable local_var4)[4]; + + int (* _Nullable __indexable local_var5)[4]; + + int (* __indexable _Nullable local_var6)[4]; +} + +struct S { + int * __single _Nullable member_var1; + // CHECK: member_var1 'int *__single _Nullable':'int *__single' + + int * _Nullable __single member_var2; + // CHECK: member_var2 'int *__single _Nullable':'int *__single' +}; + +union U { + int * __single _Nullable union_var1; + // CHECK: union_var1 'int *__single _Nullable':'int *__single' + + int * _Nullable __single union_var2; + // CHECK: union_var2 'int *__single _Nullable':'int *__single' +}; + +void foo1(int * _Nonnull __single __counted_by(n) ptr1, unsigned n); +// CHECK: ptr1 'int *__single __counted_by(n) _Nonnull':'int *__single' + +void foo2(int * __single __counted_by(n) _Nonnull ptr2, unsigned n); +// CHECK: ptr2 'int *__single __counted_by(n) _Nonnull':'int *__single' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c new file mode 100644 index 0000000000000..9c584c88f551c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c @@ -0,0 +1,41 @@ + +#include + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + +int * __ptrauth(2, 0, 0) global_var; +// CHECK: global_var 'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) global_array[5]; +// CHECK: global_array 'int *__single__ptrauth(2,0,0)[5]' + +void foo(void) { + int * __ptrauth(2, 0, 0) local_var; +} +// CHECK: local_var 'int *__bidi_indexable__ptrauth(2,0,0)' + +void foo2(void) { + int * __ptrauth(2, 0, 0) local_array[5]; +} +// CHECK: local_array 'int *__single__ptrauth(2,0,0)[5]' + +struct Foo { + int * __ptrauth(2, 0, 0) member_var; +}; +// CHECK: member_var 'int *__single__ptrauth(2,0,0)' + +struct Foo2 { + int * __ptrauth(2, 0, 0) member_array[5]; +}; +// CHECK: member_array 'int *__single__ptrauth(2,0,0)[5]' + +union U { + int * __ptrauth(2, 0, 0) union_var; +}; +// CHECK: union_var 'int *__single__ptrauth(2,0,0)' + +union U2 { + int * __ptrauth(2, 0, 0) union_array[5]; +}; +// CHECK: union_array 'int *__single__ptrauth(2,0,0)[5]' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c new file mode 100644 index 0000000000000..fb17981292572 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c @@ -0,0 +1,99 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + +#include + +int * __ptrauth(2, 0, 0) __single global_var1; +// CHECK: global_var1 'int *__single__ptrauth(2,0,0)' + +int * __single __ptrauth(2, 0, 0) global_var2; +// CHECK: global_var2 'int *__single__ptrauth(2,0,0)' + +void foo(void) { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) local_buf1; + // CHECK: local_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) local_buf2; + // CHECK: local_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) local_buf3 __counted_by(n3); + // CHECK: local_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n4; + unsigned char *__ptrauth(2, 0, 0) __counted_by(n4) local_byte_buf1; + // CHECK: local_byte_buf1 'unsigned char *__single __counted_by(n4)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + int n5; + unsigned char *__counted_by(n5) __ptrauth(2, 0, 0) local_byte_buf2; + // CHECK: local_byte_buf2 'unsigned char *__single __counted_by(n5)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + int n6; + unsigned char *__ptrauth(2, 0, 0) local_byte_buf3 __counted_by(n6); + // CHECK: local_byte_buf3 'unsigned char *__single __counted_by(n6)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' +} + +struct Foo { + int * __ptrauth(2, 0, 0) __single member_var1; + // CHECK: member_var1 'int *__single__ptrauth(2,0,0)' + + int * __single __ptrauth(2, 0, 0) member_var2; + // CHECK: member_var2 'int *__single__ptrauth(2,0,0)' + + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) member_buf1; + // CHECK: member_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int *__counted_by(n) __ptrauth(2, 0, 0) member_buf2; + // CHECK: member_buf2 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int *__ptrauth(2, 0, 0) member_buf3 __counted_by(n); + // CHECK: member_buf3 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + unsigned char *__ptrauth(2, 0, 0) __counted_by(n) member_byte_buf1; + // CHECK: member_byte_buf1 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + unsigned char *__counted_by(n) __ptrauth(2, 0, 0) member_byte_buf2; + // CHECK: member_byte_buf2 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + unsigned char *__ptrauth(2, 0, 0) member_byte_buf3 __counted_by(n); + // CHECK: member_byte_buf3 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' +}; + +union U { + int * __ptrauth(2, 0, 0) __single union_var1; + // CHECK: union_var1 'int *__single__ptrauth(2,0,0)' + + int * __single __ptrauth(2, 0, 0) union_var2; + // CHECK: union_var2 'int *__single__ptrauth(2,0,0)' +}; + +void bar(void) { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) local_buf1; + // CHECK: local_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) local_buf2; + // CHECK: local_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) local_buf3 __counted_by(n3); + // CHECK: local_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' +} + +struct Bar { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) member_buf1; + // CHECK: member_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) member_buf2; + // CHECK: member_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) member_buf3 __counted_by(n3); + // CHECK: member_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' +}; diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c new file mode 100644 index 0000000000000..d06714dcb9926 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + +#include + +int * _Nullable __ptrauth(2, 0, 0) global_var1; +// CHECK: global_var1 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) _Nullable global_var2; +// CHECK: global_var2 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c new file mode 100644 index 0000000000000..afca9bf78926e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c @@ -0,0 +1,56 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +int * _Nullable __ptrauth(2, 0, 0) __single global_var1; +// CHECK: global_var1 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * _Nullable __single __ptrauth(2, 0, 0) global_var2; +// CHECK: global_var2 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) _Nullable __single global_var3; +// CHECK: global_var3 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) __single _Nullable global_var4; +// CHECK: global_var4 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __single __ptrauth(2, 0, 0) _Nullable global_var5; +// CHECK: global_var5 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __single _Nullable __ptrauth(2, 0, 0) global_var6; +// CHECK: global_var6 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +void foo() { + int n1 = 0; + int *_Nullable __ptrauth(2, 0, 0) __counted_by(n1) local_buf11; + // CHECK: local_buf11 'int *__single __counted_by(n1) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2 = 0; + int *__ptrauth(2, 0, 0) _Nullable __counted_by(n2) local_buf12; + // CHECK: local_buf12 'int *__single __counted_by(n2)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n3 = 0; + int *__ptrauth(2, 0, 0) __counted_by(n3) _Nullable local_buf13; + // CHECK: local_buf13 'int *__single __counted_by(n3)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n4 = 0; + int *_Nullable __counted_by(n4) __ptrauth(2, 0, 0) local_buf21; + // CHECK: local_buf21 'int *__single __counted_by(n4) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n5; + int *__counted_by(n5) _Nullable __ptrauth(2, 0, 0) local_buf22; + // CHECK: local_buf22 'int *__single __counted_by(n5) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n6; + int *__counted_by(n6) __ptrauth(2, 0, 0) _Nullable local_buf23; + // CHECK: local_buf23 'int *__single __counted_by(n6)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n7; + int *_Nullable __ptrauth(2, 0, 0) local_buf31 __counted_by(n7); + // CHECK: local_buf31 'int *__single __counted_by(n7) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n8; + int *__ptrauth(2, 0, 0) _Nullable local_buf32 __counted_by(n8); + // CHECK: local_buf32 'int *__single __counted_by(n8)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' +} diff --git a/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c b/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c new file mode 100644 index 0000000000000..e4fd68288a0a9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void fnull_terminated(const char *); +void fnull_terminated_explicit(const char *__null_terminated); +// expected-note@+3{{passing argument to parameter here}} +// expected-note@+2{{passing argument to parameter here}} +// expected-note@+1{{passing argument to parameter here}} +void fnull_terminated_iptr(const int *__null_terminated); +// expected-note@+3{{passing argument to parameter here}} +// expected-note@+2{{passing argument to parameter here}} +// expected-note@+1{{passing argument to parameter here}} +void fterminated_by_minus_one(const int *__terminated_by(-1)); + +void test(void) { + fnull_terminated(__func__); + fnull_terminated(__FUNCTION__); + fnull_terminated(__PRETTY_FUNCTION__); + + fnull_terminated_explicit(__func__); + fnull_terminated_explicit(__FUNCTION__); + fnull_terminated_explicit(__PRETTY_FUNCTION__); + + // expected-error@+3{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__func__); + // expected-error@+3{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__FUNCTION__); + // expected-error@+3{{passing 'const char[16]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__PRETTY_FUNCTION__); + + // expected-error@+1{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__func__); + // expected-error@+1{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__FUNCTION__); + // expected-error@+1{{passing 'const char[16]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__PRETTY_FUNCTION__); +} + +void test_assign(const char *fname) { + fname = __func__; + fname = __FUNCTION__; + fname = __PRETTY_FUNCTION__; + + const char *__null_terminated lfname1 = __func__; + const char *__null_terminated lfname2 = __FUNCTION__; + const char *__null_terminated lfname3 = __PRETTY_FUNCTION__; + + const char *lfname_non_null_terminated1 = __func__; + const char *lfname_non_null_terminated2 = __FUNCTION__; + const char *lfname_non_null_terminated3 = __PRETTY_FUNCTION__; + + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one1 = __func__; + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one2 = __FUNCTION__; + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one3 = __PRETTY_FUNCTION__; + +} diff --git a/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c b/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c new file mode 100644 index 0000000000000..e9b3324160825 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c @@ -0,0 +1,159 @@ +// XFAIL: * +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// TODO: ended_by +#include + +void out_ptr_out_count(int *__counted_by(*out_count) *out_ptr, + unsigned *out_count); + +void calls_out_ptr_out_count(void) { + // with null out buffer and null out count. + out_ptr_out_count(0, 0); + + // with non-null out buffer and null out count. + // no error?? + int *__counted_by(4) p1; + out_ptr_out_count(&p1, 0); + + // with null out buffer and non-null out count. + // reading `*out_count` is fine in this case, but updating `*out_count` + // will require updating `*out_ptr` so allowing this is not useful. + unsigned count1; + int *__counted_by(count1) p2; + out_ptr_out_count(0, &count1); + + // with null out buffer and unrelated out count. + unsigned count2; + out_ptr_out_count(0, &count2); + + // with incompatible out buffer and null out count. + // `*out_ptr` will generate promotion to bidi_indexable using `*out_count`, + // which will lead to null pointer dereference. + int *__single p3; + out_ptr_out_count(&p3, 0); + + unsigned count4; + int *__counted_by(count4) p4; + out_ptr_out_count(&p4, 0); +} + +void out_ptr_in_ptr_out_count(int *__counted_by(*out_count) *out_ptr, + int *__counted_by(*out_count) ptr, + unsigned *out_count); + +void calls_out_ptr_in_ptr_out_count(void) { + // not allowed due to `ptr`. + out_ptr_in_ptr_out_count(0, 0, 0); + int *p1; + out_ptr_in_ptr_out_count(0, p1, 0); +} + +void out_ptr2_out_count(int *__counted_by(*out_count) *out_ptr1, + int *__counted_by(*out_count) *out_ptr2, + unsigned *out_count); + +void calls_out_ptr2_out_count(void) { + // should be fine + out_ptr2_out_count(0, 0, 0); + + int *__counted_by(4) p1; + out_ptr2_out_count(&p1, 0, 0); + + int *__counted_by(0) p2; + out_ptr2_out_count(0, &p2, 0); + + // doesn't fit in the current model because updating `*out_ptr1` or + // `*out_count` would require self assignment `*out_ptr2 = *out_ptr2`, + // which involves null pointer dereference. + unsigned count3; + int *__counted_by(count3) p3; + out_ptr2_out_count(&p3, 0, &count3); + + unsigned count4; + int *__counted_by(count4) p4; + out_ptr2_out_count(0, &p4, 0); +} + +// ptr promotion to bidi_indexable will access `*out_count`, whether +// `ptr` is null or not. Thus, `out_count` shouldn't be null. +void in_ptr_out_count(int *__counted_by(*out_count) ptr, unsigned *out_count); + +void calls_in_ptr_out_count(void) { + // no error?? + // with null buffer and null out count. + in_ptr_out_count(0, 0); + + // with non-null buffer and null out count. + // no error?? + int *p1; + in_ptr_out_count(p1, 0); + + // with null buffer and non-null out count. + unsigned count1; + int *__counted_by(count1) p2; + in_ptr_out_count(0, &count1); + + // with null buffer and unrelated out count. + // no error?? + unsigned count2; + in_ptr_out_count(0, &count2); +} + +// -fbounds-safety doesn't support inout buffer. +void out_ptr_in_count(int *__counted_by(count) *out_ptr, unsigned count); + +void calls_out_ptr_in_count(void) { + // with null buffer and null count. + // no error? + in_ptr_out_count(0, 0); + + // with null buffer and non-null count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + in_ptr_out_count(0, 2); + + // with non-null buffer and null count. + int *__counted_by(4) p1; + in_ptr_out_count(&p1, 0); + + // weird error: non-pointer to safe pointer conversion is not allowed...? + int *__counted_by(4) p2; + in_ptr_out_count(&p2, 4); + + // with incompatible, non-null buffer and null count. + // no error? + int *__single p3; + in_ptr_out_count(&p3, 0); + + unsigned count1; + int *__counted_by(count1) p4; + in_ptr_out_count(&p4, 0); + + // with null buffer and non-null count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + unsigned count2; + int *__counted_by(count2) p5; + in_ptr_out_count(0, count2); + + // with null buffer and unrelated out count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + unsigned count3; + in_ptr_out_count(0, count3); +} + +void out_ptr_in_ptr_in_count(int *__counted_by(count) *out_ptr, + int *__counted_by(count) ptr, + unsigned count); + +void calls_out_ptr_in_ptr_in_count(void) { + // should be fine + out_ptr_in_ptr_in_count(0, 0, 0); + + // should be a warning/error for in ptr + out_ptr_in_ptr_in_count(0, 0, 10); + + // should be fine + int arr[10]; + out_ptr_in_ptr_in_count(0, arr, 10); +} diff --git a/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c b/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c new file mode 100644 index 0000000000000..f9ee933fa9359 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef int * unspec_ptr_t; +typedef int *__single single_ptr_t; +typedef int *__bidi_indexable bidi_ptr_t; + +unspec_ptr_t __bidi_indexable g_bidi; + +void foo(unspec_ptr_t __bidi_indexable a_bidi_ok) { + unspec_ptr_t __single l_single_ok; + single_ptr_t __single l_single_single_ok; + // expected-error@+1{{pointer cannot have more than one bound attribute}} + single_ptr_t __bidi_indexable l_single_bidi_fail; +} diff --git a/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c b/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c new file mode 100644 index 0000000000000..38b60bd96cd59 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void foo(int *unqual, int *__bidi_indexable bidi, int *__indexable fwd, + int *__single single, int *__unsafe_indexable unsafe) { + (void)__builtin_get_pointer_lower_bound(unqual); + (void)__builtin_get_pointer_lower_bound(bidi); + (void)__builtin_get_pointer_lower_bound(fwd); + (void)__builtin_get_pointer_lower_bound(single); + // expected-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + (void)__builtin_get_pointer_lower_bound(unsafe); +} diff --git a/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c b/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c new file mode 100644 index 0000000000000..bcca0256d35b0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c @@ -0,0 +1,26 @@ + +// Use arm64-apple-ios to ensure that sizeof(uint8_t) < sizeof(uintptr_t). +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +char buf[42]; + +// ok +uintptr_t buf_ptr = (uintptr_t)buf; + +// expected-error@+2{{initializer element is not a compile-time constant}} +// expected-warning@+1{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'char *__bidi_indexable'}} +uint8_t buf_8 = (uint8_t)buf; + +void foo(int *__single s, int *__indexable i, int *__bidi_indexable bi) { + uintptr_t s_ptr = (uintptr_t)s; // ok + uintptr_t i_ptr = (uintptr_t)i; // ok + uintptr_t bi_ptr = (uintptr_t)bi; // ok + + uint8_t s_8 = (uint8_t)s; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__single'}} + uint8_t i_8 = (uint8_t)i; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__indexable'}} + uint8_t bi_8 = (uint8_t)bi; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__bidi_indexable'}} +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c new file mode 100644 index 0000000000000..25edc2c760eec --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int* foo(int *__single a) { + ++a; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-2{{pointer 'a' declared here}} + return a++; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-4{{pointer 'a' declared here}} +} + +int main() { + int *__indexable ap; + ap--; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + --ap; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + int *inc = foo(ap); + return *inc; +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c new file mode 100644 index 0000000000000..6899e51733d51 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int* foo(int *a) { + ++a; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-2{{pointer 'a' declared here}} + return a++; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-4{{pointer 'a' declared here}} +} + + +int main() { + int arr[1]; + int *__indexable ap = arr; + ap--; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + --ap; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + int *inc = foo(arr); + return *inc; +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c b/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c new file mode 100644 index 0000000000000..5a900492338f7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +struct Foo { int *buf; int cnt; }; + +void Test(struct Foo *f) { + // expected-error@+2{{pointer arithmetic on single pointer 'f->buf' is out of bounds; consider adding '__counted_by' to 'Foo::buf'}} + // expected-note@-4{{pointer 'Foo::buf' declared here}} + int *ptr = f->buf + 2; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ptr-arith.c b/clang/test/BoundsSafety/Sema/ptr-arith.c new file mode 100644 index 0000000000000..880a554c1488c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int a = -3; + +int foo() { + int buf[10]; + int *__bidi_indexable ptrBound = buf; + int *__single ptrThin = &ptrBound[1]; + int *__indexable ptrArray = &ptrBound[0]; + + ptrBound[9] = 3; // ok + + ptrArray[a] = 5; // run-time error + ptrArray[2] = 2; // ok + ptrArray[-1] = 3; // expected-error{{array subscript with a negative index on indexable pointer 'ptrArray' is out of bounds}} + + a[ptrArray] = 5; // run-time error + 2[ptrArray] = -1; // ok + a[ptrArray] = 5[ptrThin]; // expected-error{{array subscript on single pointer 'ptrThin' must use a constant index of 0 to be in bounds}} + -3[ptrArray] = 2; // expected-error{{expression is not assignable}} + + ptrThin = ptrArray + (-2); // expected-error{{decremented indexable pointer 'ptrArray' is out of bounds}} + ptrThin = ptrArray + 4; // ok + + ptrBound = ptrThin + 3; // expected-error{{pointer arithmetic on single pointer 'ptrThin' is out of bounds; consider adding '__counted_by' to 'ptrThin'}} + // expected-note@-18{{pointer 'ptrThin' declared here}} + + // With casts + // TODO(dliew): Support suggesting `__counted_by` on the `ptrThin` decl in the presence of this cast. + char *tmp = ((char *)ptrThin) + 1; // expected-error-re{{pointer arithmetic on single pointer '((char *)ptrThin)' is out of {{bounds$}}}} + + return ptrThin[2]; // expected-error{{array subscript on single pointer 'ptrThin' must use a constant index of 0 to be in bounds}} +} + diff --git a/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c b/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c new file mode 100644 index 0000000000000..7df08a4d4c04c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + char *__bidi_indexable __ptrauth(1, 1, 1) inner1; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__indexable __ptrauth(1, 1, 1) inner2; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __bidi_indexable inner3; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __indexable inner4; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __single inner5; + char *__ptrauth(1, 1, 1) __unsafe_indexable inner6; +}; diff --git a/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c b/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c new file mode 100644 index 0000000000000..9cfa208e18ffa --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c @@ -0,0 +1,40 @@ +#include + +// ptrcheck macros should not affect code when compiled without -fbounds-safety +// RUN: %clang_cc1 -fsyntax-only -pedantic -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fbounds-safety %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fbounds-safety -D TEST_UNSAFE %s -verify=unsafe + +// ptcheck macros should not affect code when compiled with -fexperimental-bounds-safety-attributes + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c++ %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c++ -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c++ %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c++ -D TEST_UNSAFE %s -verify=expected + +// expected-no-diagnostics + +int * __ptrcheck_unavailable unsafe_func(void); // unsafe-note{{'unsafe_func' has been explicitly marked unavailable here}} +int * __ptrcheck_unavailable_r(safe_func) unsafe_func2(void); // unsafe-note{{'unsafe_func2' has been explicitly marked unavailable here}} +void safe_func(int *__counted_by(*out_size) * out_arr, int *out_size); + +#ifdef TEST_UNSAFE +void test1(void) { + int *buf = unsafe_func(); // unsafe-error{{'unsafe_func' is unavailable: unavailable with -fbounds-safety}} + buf = unsafe_func2(); // unsafe-error{{'unsafe_func2' is unavailable: unavailable with -fbounds-safety. Use safe_func instead.}} +} +#endif + +void test2(void) { + int len; + int * __counted_by(len) buf; + safe_func(&buf, &len); +} diff --git a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c new file mode 100644 index 0000000000000..a987c3a0a88b9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#define __counted_by(N) __attribute__((__counted_by__(N))) + +struct T { + unsigned n; + int * end; + // expected-error@+2{{expected ';' at end of declaration list}} + // expected-error@+1{{a parameter list without types is only allowed in a function definition}} + int *__counted_by(3) ended_by(end) ptr; +}; \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c new file mode 100644 index 0000000000000..a484296bf12b2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c @@ -0,0 +1,110 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(int *__counted_by(len) __counted_by(len+2)* buf, int len); // expected-error{{pointer cannot have more than one count attribute}} +void bar(int **__counted_by(len) buf __counted_by(len + 1), int len); // expected-error{{pointer cannot have more than one count attribute}} +int *__counted_by(len) __counted_by(len + 2) baz(int len); // expected-error{{pointer cannot have more than one count attribute}} + +int *__counted_by(len) bazx(int len); +int *__counted_by(len) bazx(int len); // ok - same count expr +int *__counted_by(len) bazx(int len) {} // ok - same count expr + +int *__counted_by(4) bazz(); +int *__counted_by(4) bazz(); // ok - same count expr +int *__counted_by(4) bazz() {} // ok - same count expr + +// expected-note@+1{{previous declaration is here}} +int *bazy(); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(4) bazy() {} // ok + +// expected-note@+2{{previous declaration is here}} +// expected-note@+1{{previous declaration is here}} +int *__counted_by(len) bayz(unsigned len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *bayz(unsigned len) {} +// expected-error@+1{{conflicting '__sized_by' attribute with the previous function declaration}} +int *__sized_by(len) bayz(unsigned len) {} + +// expected-note@+1{{previous declaration is here}} +int *baxz(unsigned len, int **pptr); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *baxz(unsigned len, int *__counted_by(len)* pptr) {} // ok + +// expected-note@+1{{previous declaration is here}} +int *baxy(unsigned len, int *__counted_by(len)* pptr); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *baxy(unsigned len, int ** pptr) {} + +char *__counted_by(len) qux(int len); +// expected-note@+1{{previous declaration is here}} +char *__sized_by(len) qux(int len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +char *__counted_by(4) qux(int len) {} + +int *__counted_by(len) __counted_by(len) quux(int len) {} // ok - same count expr + +int *__counted_by(len) __counted_by(cnt) quuz(int len, int cnt) {} // expected-error{{pointer cannot have more than one count attribute}} + +void corge(int *__counted_by(len) ptr, int len); +void corge(int *__counted_by(len) ptr, int len); // ok +void corge(int *__counted_by(len) ptr, int len) {} // ok + +// expected-note@+1{{previous declaration is here}} +int ** grault(int len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(len) grault(int len) {} + +int ** grault2(int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int *__counted_by(len)* grault2(int len) {} + +struct S { + int *__counted_by(l) bp2 __counted_by(l+1); // expected-error{{pointer cannot have more than one count attribute}} + int l; +}; + +struct T { + unsigned n; + int * end; + int *__counted_by(n) __ended_by(end) ptr; // expected-error{{pointer cannot have count and range at the same time}} + int *__ended_by(end) __counted_by(n) ptr2; // expected-error{{pointer cannot have count and range at the same time}} +}; + +void end1(int *__ended_by(b) a, int *b); // OK +void end1(int *__ended_by(b) a, int *b); // OK redeclaration + +// expected-note@+3{{previous declaration is here}} \ + expected-note@+3{{previous declaration is here}} \ + expected-note@+3{{previous declaration is here}} +void end1(int *__ended_by(b) a, int *b) {} // OK definition over declaration + +void end1(int *a, int *b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} +void end1(int *a, int *__ended_by(a) b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} +void end1(int *__ended_by(b) a, int *__ended_by(a) b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} + +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c); // OK +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c); // OK + +// expected-note@+1{{previous declaration is here}} +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c) {} // OK definition over declaration + +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *__ended_by(a) c); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} + +#ifdef __started_by +// this is not currently compiled; it's meant to break if/when __started_by +// explicitly becomes a thing +void start(int *a, int *__started_by(a) b); // OK +void start(int *a, int *__started_by(a) b); // OK redeclaration +// expected-note@+1{{previous declaration is here}} expected-note@+1{{previous declaration is here}} +void start(int *a, int *__started_by(a) b); // OK definition over declaration + +void start(int *a, int *__started_by(a) b); // expected-error{{conflicting '__started_by' attribute with the previous function declaration}} +void start(int *a, int *__started_by(a) b); // expected-error{{conflicting '__started_by' attribute with the previous function declaration}} +#endif diff --git a/clang/test/BoundsSafety/Sema/redundant-attrs.c b/clang/test/BoundsSafety/Sema/redundant-attrs.c new file mode 100644 index 0000000000000..0c89703b0a9eb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/redundant-attrs.c @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +#include "redundant-attrs.h" + +int *__bidi_indexable __bidi_indexable ptrBoundBound; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +int *__indexable __bidi_indexable ptrArrayBound; // expected-error{{pointer cannot have more than one bound attribute}} +int *__indexable __single ptrArrayThin; // expected-error{{pointer cannot have more than one bound attribute}} +int *__bidi_indexable *__indexable ptrBoundPtrArray; +int *__bidi_indexable *__bidi_indexable ptrBoundPtrBound; +int *__indexable __single *ptrArrayThinCast; // expected-error{{pointer cannot have more than one bound attribute}} +int *__bidi_indexable __single *ptrBoudCastThin; // expected-error{{pointer cannot have more than one bound attribute}} + +// Using `#pragma clang abi_ptr_attr set(X Y)` must fail if X and Y are different +// attributes, but you must be able to use `#pragma clang abi_ptr_attr set(X)` and +// then `#pragma clang abi_ptr_attr set(Y)` (which the macros below expand to). +__ptrcheck_abi_assume_unsafe_indexable() +__ptrcheck_abi_assume_single() // Only default, explicit bounds override instead of conflict. + +int *__unsafe_indexable foo(int * __unsafe_indexable x) { + int * __unsafe_indexable * __unsafe_indexable __unsafe_indexable y = &x; // expected-warning{{pointer annotated with __unsafe_indexable multiple times. Annotate only once to remove this warning}} + return *y; +} + +// expected-error@+1{{pointer cannot have more than one bound attribute}} +_Pragma("clang abi_ptr_attr set(single unsafe_indexable)") +void bar(int * x); + +typedef int *__bidi_indexable bidiPtr; +typedef int *__bidi_indexable __bidi_indexable bidiBidiPtr; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +bidiPtr __bidi_indexable ptrBoundBound2; + +#define bidiPtr2 int *__bidi_indexable +bidiPtr2 __bidi_indexable ptrBoundBound3; +bidiPtr2_header __bidi_indexable ptrBoundBound3_header; + +typedef int *intPtr; +intPtr __single __single ptrBoundBound4; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +char * __null_terminated __null_terminated ptrBoundBound5; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +int len = 0; +int * __counted_by(len) __counted_by(len) ptrBoundBound6; // expected-error{{pointer cannot have more than one count attribute}} + +struct S1 { + int size; + int arrBoundBound[__counted_by(size) __counted_by(size)]; // expected-error{{array cannot have more than one count attribute}} +}; +struct S2 { + int size; + int len; + int arrBoundBound[__counted_by(size) __counted_by(len)]; // expected-error{{array cannot have more than one count attribute}} +}; + +#define intPtr2 int * +intPtr2 __single __single ptrBoundBound7; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +intPtr2_header __single __single ptrBoundBound7_header; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +int * __counted_by(len) __counted_by(len) ptrBoundBound8; // expected-error{{pointer cannot have more than one count attribute}} + +bidiPtr __indexable ptrBoundBoundMismatch; // expected-error{{pointer cannot have more than one bound attribute}} +bidiPtr2 __indexable ptrBoundBoundMismatch2; // expected-error{{pointer cannot have more than one bound attribute}} +bidiPtr2_header __indexable ptrBoundBoundMismatch3; // expected-error{{pointer cannot have more than one bound attribute}} + +#define BIDI_INDEXABLE(T, X) do { T __bidi_indexable X; } while (0) +void macro_decl(void) { + BIDI_INDEXABLE(int * __bidi_indexable, varName); +} + +#define BIDI_INDEXABLE2(X) do { int * __bidi_indexable X; } while (0) +void macro_decl2(void) { + BIDI_INDEXABLE2(__bidi_indexable varName); +} + +void macro_decl3(void) { + BIDI_INDEXABLE(int * __bidi_indexable __bidi_indexable, varName); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} + BIDI_INDEXABLE_header(int * __bidi_indexable __bidi_indexable, varName_header); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +} + +void macro_decl4(void) { + BIDI_INDEXABLE2(__bidi_indexable __bidi_indexable varName); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} + BIDI_INDEXABLE2_header(__bidi_indexable __bidi_indexable varName_header); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +} + +int * __single * __single __single ptrBoundBoundNested; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +int * __single __single * __single ptrBoundBoundNested2; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +_Atomic(int * __single) __single * __single ptrBoundBoundNestedAtomic; +_Atomic(int * __single) __single __single * __single ptrBoundBoundNestedAtomic2; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +_Atomic(int * __single __single) __single * __single ptrBoundBoundNestedAtomic3; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} + +int * __null_terminated * __null_terminated __null_terminated ptrBoundBoundNestedNullTerm; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int * __null_terminated __null_terminated * __null_terminated ptrBoundBoundNestedNullTerm2; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +typedef int *__null_terminated nullTermPtr; +nullTermPtr __null_terminated * __null_terminated ptrBoundBoundNestedNullTermTypedef; +nullTermPtr __null_terminated __null_terminated * __null_terminated ptrBoundBoundNestedNullTermTypedef2; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermPtr __terminated_by(1) ptrTerm0Term1; // expected-error{{pointer cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '1'}} + +typedef int *__null_terminated __null_terminated nullTermNullTermPtr; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermNullTermPtr * __null_terminated ptrBoundBoundNestedNullTermTypedef3; + +#define nullTermPtr2 int *__null_terminated +nullTermPtr2 __null_terminated ptrNullTermNullTerm; +nullTermPtr2_header __null_terminated ptrNullTermNullTerm_header; + +int * __attribute__((aligned (16))) __single ptrUnrelatedAttributeBound; +int * __null_terminated __attribute__((aligned (16))) __null_terminated ptrUnrelatedAttributeBoundBound; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermPtr __attribute__((aligned (16))) __null_terminated ptrUnrelatedAttributeBoundBound2; + +int * __attribute__((__bidi_indexable__)) __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +#define nullTermPtr3 int * __attribute__((__bidi_indexable__)) +nullTermPtr3 __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro2; +nullTermPtr3_header __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro3; + +int *__terminated_by(0) __terminated_by(0u) ptrTerminatedByDifferentSignedness; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int *__terminated_by(0) __terminated_by(0l) ptrTerminatedByDifferentWidth; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int *const __terminated_by(0) _Nullable __terminated_by(0) * _Nullable __terminated_by(0) ptrTerminatedNestedAndNullable; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +typedef int *_Nullable __bidi_indexable nullableBidiPtr; +nullableBidiPtr __single ptrNullableBidiSingleConflict; //expected-error{{pointer cannot have more than one bound attribute}} + +typedef int *_Nullable nullablePtr; +nullablePtr __single ptrAttributedTypeSeparateDecl; + +typedef int *_Nullable __null_terminated __bidi_indexable nullableNTBidiPtr; //expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} +nullableNTBidiPtr ptrNullableNTBidi; diff --git a/clang/test/BoundsSafety/Sema/redundant-attrs.h b/clang/test/BoundsSafety/Sema/redundant-attrs.h new file mode 100644 index 0000000000000..35115bd714be1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/redundant-attrs.h @@ -0,0 +1,6 @@ +#define bidiPtr2_header int *__bidi_indexable +#define intPtr2_header int * +#define BIDI_INDEXABLE_header(T, X) do { T __bidi_indexable X; } while (0) +#define BIDI_INDEXABLE2_header(X) do { int * __bidi_indexable X; } while (0) +#define nullTermPtr2_header int *__null_terminated +#define nullTermPtr3_header int * __attribute__((__bidi_indexable__)) diff --git a/clang/test/BoundsSafety/Sema/self-assign.c b/clang/test/BoundsSafety/Sema/self-assign.c new file mode 100644 index 0000000000000..4cb4a92fe0607 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/self-assign.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wself-assign -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wself-assign -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// -Wself-assign should not trigger for self-assignments of variables that have +// external bounds, or on the external bounds of those variables. + +void count(int *__counted_by(count) p, int count) { + p = p; + count = count; + if (1) { + int count2 = count; + int *__counted_by(count2) p2 = p; + count2 = count2; + p2 = p2; + } +} + +void end(int *__ended_by(end) begin, int *end) { + begin = begin; + end = end; + if (1) { + int *end2 = end; + int *__ended_by(end2) begin2 = begin; + begin2 = begin2; + end2 = end2; + } +} + +void unrelated(int unrelated) { + unrelated = unrelated; // expected-warning{{explicitly assigning value of variable of type 'int' to itself}} +} diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c new file mode 100644 index 0000000000000..30e22f25d7479 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c @@ -0,0 +1,880 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include +#include + +//============================================================================== +// Constant 0 count +//============================================================================== + +// not a trap +void cb_const_size_zero(int *__counted_by(0) buf) {} +int *__counted_by(0) use_cb_const_size_zero(int *buf) { + int *local = buf; + cb_const_size_zero(local); + + int *__counted_by(0) local2 = local; + return local; +} + +// not a trap +void cbon_const_size_zero(int *__counted_by_or_null(0) buf) {} +int *__counted_by_or_null(0) use_cbon_const_size_zero(int *buf) { + int *local = buf; + cbon_const_size_zero(local); + + int *__counted_by_or_null(0) local2 = local; + return local; +} + +// not a trap +void sb_const_size_zero(void *__sized_by(0) buf) {} +void *__sized_by(0) use_sb_const_size_zero(int *buf) { + int *local = buf; + sb_const_size_zero(local); + + void *__sized_by(0) local2 = local; + return local; +} + +// not a trap +void sbon_const_size_zero(void *__sized_by_or_null(0) buf) {} +void *__sized_by_or_null(0) use_sbon_const_size_zero(int *buf) { + int *local = buf; + sbon_const_size_zero(local); + + void *__sized_by_or_null(0) local2 = local; + return local; +} + +//============================================================================== +// __sized_by constant count sizeof(int) +//============================================================================== + +// not a trap (provided buf != null) +void sb_const_sizeof_int(void *__sized_by(sizeof(int)) buf) {} +void *__sized_by(sizeof(int)) use_sb_const_sizeof_int(int *buf) { + int *local = buf; + sb_const_sizeof_int(local); + + void *__sized_by(sizeof(int)) local2 = local; + return local; +} + +// trap - false negative warning +void *__sized_by(sizeof(int)) use_sb_const_sizeof_int_null_init(int *buf) { + int *local = 0; + sb_const_sizeof_int(local); + + void *__sized_by(sizeof(int)) local2 = local; + return local; +} + + +// not a trap +void sbon_const_sizeof_int(void *__sized_by_or_null(sizeof(int)) buf) {} +void *__sized_by_or_null(sizeof(int)) use_sbon_const_sizeof_int(int *buf) { + int *local = buf; + sbon_const_sizeof_int(local); + + void *__sized_by_or_null(sizeof(int)) local2 = local; + return local; +} + +//============================================================================== +// __sized_by constant count sizeof(int)+1 +//============================================================================== + +// trap +void sb_const_sizeof_int_plus_one(void *__sized_by(sizeof(int)+1) buf) {} +void *__sized_by(sizeof(int)+1) use_sb_const_sizeof_int_plus_one(int *buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(5UL)' (aka 'void *__single') requires 5 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sb_const_sizeof_int_plus_one(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void *__sized_by(sizeof(int)+1) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + + +// trap if local != null +void sbon_const_sizeof_int_plus_one(void *__sized_by_or_null(sizeof(int)+1) buf) {} +void *__sized_by_or_null(sizeof(int)+1) use_sbon_const_sizeof_int_plus_one(int *buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') requires 5 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sbon_const_sizeof_int_plus_one(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void *__sized_by_or_null(sizeof(int)+1) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + +// not a trap +void use_sbon_const_sizeof_int_plus_one_null_ptr_reach_call(int *buf) { + int *local = (int* __single) 0; + sbon_const_sizeof_int_plus_one(local); // At callsite we know the pointer is null. +} + + + +//============================================================================== +// __counted_by constant count 1 +//============================================================================== + +// not a trap (provided local != null) +void cb_const_size_one(int *__counted_by(1) buf) {} +int *__counted_by(1) use_cb_const_size_one(int* buf) { + int *local = buf; + cb_const_size_one(local); + + int *__counted_by(1) local2 = local; + return local; +} + + + +// not a trap +void cbon_const_size_one(int *__counted_by_or_null(1) buf) {} +int *__counted_by_or_null(1) use_cbon_const_size_one(int* buf) { + int *local = buf; + cbon_const_size_one(local); + + int *__counted_by_or_null(1) local2 = local; + return local; +} + + +//============================================================================== +// __counted_by constant count > 1 +//============================================================================== + +// trap +void cb_const_size_two(int *__counted_by(2) buf) {} +int *__counted_by(2) use_cb_const_size_two(int* buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(2)' (aka 'int *__single') requires 8 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cb_const_size_two(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by(2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + +// trap if ptr != null +void cbon_const_size_two(int *__counted_by_or_null(2) buf) {} +int *__counted_by_or_null(2) use_cbon_const_size_two(int* buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') requires 8 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cbon_const_size_two(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by_or_null(2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + + +// trap +_Static_assert(sizeof(long long int) > sizeof(int), "unexpected diff"); +void cb_const_size_two_ll(long long int *__counted_by(2) buf) {} +long long int *__counted_by(2) use_cb_const_size_two_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by(2)' (aka 'long long *__single') requires 16 bytes or more}} + int *local = buf; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + cb_const_size_two_ll((long long int*)local); + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + long long int *__counted_by(2) local2 = (long long int*) local; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + return (long long int*)local; +} + +void cb_const_size_one_ll(long long int *__counted_by(1) buf) {} +long long int *__counted_by(1) use_cb_const_size_one_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by(1)' (aka 'long long *__single') requires 8 bytes or more}} + // expected-note@+1 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + int *local = buf; + + // expected-warning@+2{{passing __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + cb_const_size_one_ll((long long int*) local); + + // expected-warning@+2{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + long long int *__counted_by(1) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + + +// trap if ptr != null +void cbon_const_size_two_ll(long long int *__counted_by_or_null(2) buf) {} +long long int *__counted_by_or_null(2) use_cbon_const_size_two_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') requires 16 bytes or more}} + int *local = buf; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + cbon_const_size_two_ll((long long int*)local); + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + long long int *__counted_by_or_null(2) local2 = (long long int*) local; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + return (long long int*)local; +} + +//============================================================================== +// Non const size in type +// __counted_by/__counted_by_or_null +//============================================================================== + +void cb_non_const_size(int *__counted_by(size) buf, size_t size) {} +void cbon_non_const_size(int *__counted_by_or_null(size) buf, size_t size) {} + + +// - if ptr != null then traps if size != 1 and size != 0 +// - if ptr null then traps if size != 0 +int *__counted_by(size) use_cb_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + + +// if ptr null, never traps +// if ptr != null then traps if size != 1 and size != 0 +int *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + cbon_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// not a trap for call +// maybe a trap for return +int *__counted_by(*size) use_cb_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 0. + cb_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size2` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 0. + const size_t size3 = 0; + int *__counted_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// not a trap +int *__counted_by_or_null(*size) use_cbon_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 0. + cbon_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size2` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 0. + const size_t size3 = 0; + int *__counted_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// traps iff ptr == null +int *__counted_by(*size) use_cb_non_const_size_one_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap in most cases because the analysis knows that `size` is 1. + // Currently doesn't warn about the trap when local == null. + cb_non_const_size(local, 1); + + // False positive + // The analysis doesn't know that `size2` is 1 + size_t size2 = 1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 1. + const size_t size3 = 1; + int *__counted_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// not a trap +int* __counted_by_or_null(*size) use_cbon_non_const_size_one_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 1. + cbon_non_const_size(local, 1); + + // False positive + // The analysis doesn't know that `size2` is 1 + size_t size2 = 1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 1. + const size_t size3 = 1; + int *__counted_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 1 + *size = 1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// trap +int* __counted_by(*size) use_cb_non_const_size_two_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(size)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(2UL)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cb_non_const_size(local, 2); + + // The analysis isn't aware that `size2` is 2 so the warning is not as + // specific as it should be. + size_t size2 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // The analysis is aware that `size` is 2 so the warning is more specific + const size_t size3 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2UL)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by(size3) local3 = local; + + // The analysis isn't aware that `*size` is 2 so the warning is not as + // specific as it should be. + *size = 2; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// trap if ptr != null +int* __counted_by_or_null(*size) use_cbon_non_const_size_two_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(2UL)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cbon_non_const_size(local, 2); + + // The analysis isn't aware that `size` is 2 so the warning is not as + // specific as it should be. + size_t size2 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // The analysis is aware that `size` is 2 so the warning is more specific + const size_t size3 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2UL)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by_or_null(size3) local3 = local; + + // The analysis isn't aware that `*size` is 2 so the warning not as specific + // as it should be. + *size = 2; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// - if ptr != null then traps if size > 0 +// - if ptr null then traps if size > 0 +void cb_non_const_size_ll(long long int* __counted_by(size) local, size_t size); +long long int *__counted_by(size) use_cb_non_const_size_non_const_arg_ll(int *buf, size_t size) { // expected-note 7{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) < sizeof(long long int)) not matching. + + // expected-note@+3 7{{pointer 'local' declared here}} + // expected-note@+2 4{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size_ll((long long int*) local, size); + + // FIXME: Consider suppressing the cast warning if it flows into something + // we know won't trap. + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + cb_non_const_size_ll((long long int*) local, 0); // not a trap + + size_t size2 = size; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + long long int *__counted_by(size2) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + +void cb_non_const_size_c(char* __counted_by(size) local, size_t size); +char *__counted_by(size) use_cb_non_const_size_non_const_arg_c(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) > sizeof(char)) not matching. + + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size_c((char*) local, size); + + cb_non_const_size_c((char*) local, 4); // not a trap + cb_non_const_size_c((char*) local, 3); // not a trap + cb_non_const_size_c((char*) local, 2); // not a trap + cb_non_const_size_c((char*) local, 1); // not a trap + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by(size2)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + char *__counted_by(size2) local2 = (char*) local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'char *__single __counted_by(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + return (char*) local; +} + +// - if ptr != null then traps if size > 0 +void cbon_non_const_size_ll(long long int* __counted_by_or_null(size) local, size_t size); +long long int *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg_ll(int *buf, size_t size) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) < sizeof(long long int)) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by_or_null(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + cbon_non_const_size_ll((long long int*) local, size); + + size_t size2 = size; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by_or_null(size2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + long long int *__counted_by_or_null(size2) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'long long *__single __counted_by_or_null(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + +void cbon_non_const_size_c(char* __counted_by_or_null(size) local, size_t size); +char *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg_c(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) > sizeof(char)) not matching. + + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by_or_null(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + cbon_non_const_size_c((char*) local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by_or_null(size2)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + char *__counted_by_or_null(size2) local2 = (char*) local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'char *__single __counted_by_or_null(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + return (char*) local; +} + +//============================================================================== +// Non const size in type +// __sized_by/__sized_by_or_null +//============================================================================== + +void sb_non_const_size(void *__sized_by(size) buf, size_t size) {} +void sbon_non_const_size(void *__sized_by_or_null(size) buf, size_t size) {} + +// Check the size because the warning text mentions the size explicitly. +_Static_assert(sizeof(int) == 4, "int has unexpected size"); + +// - if ptr != null then traps if size > sizeof(int) and size != 0 +// - if ptr null then traps if size != 0 +void *__sized_by(size) use_sb_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + sb_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void *__sized_by(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + + +// - if ptr != null then traps if size > sizeof(int) and size != 0 +// - if ptr null then won't trap +void *__sized_by_or_null(size) use_sbon_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + sbon_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void *__sized_by_or_null(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + +// not a trap +void *__sized_by(*size) use_sb_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sb_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void *__sized_by(size2) local2 = local; + + // The analysis knows that `size3` is 0 so it doesn't warn in this case. + const size_t size3 = 0; + void *__sized_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// not a trap +void* __sized_by_or_null(*size) use_sbon_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sbon_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + // The analysis knows that `size3` is 0 so it doesn't warn in this case. + const size_t size3 = 0; + void* __sized_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + + +// traps only if `local` is null +void* __sized_by(*size) use_sb_non_const_sizeof_int_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // traps only if `local` is null. We currently don't warn about this. + sb_non_const_size(local, sizeof(int)); + + // False positive + // The analysis doesn't know that `size` is `sizeof(int)` + size_t size2 = sizeof(int); + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void* __sized_by(size2) local2 = local; + + // traps only if `local` is null. We currently don't warn about this. + // The analysis knows that `size3` is `sizeof(int)` so it doesn't warn in + // this case. + const size_t size3 = sizeof(int); + void* __sized_by(size3) local3 = local; + + + // False positive + // The analysis doesn't know that `*size` is `sizeof(int)` + *size = sizeof(int); + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// not a trap +void* __sized_by_or_null(*size) use_sbon_non_const_sizeof_int_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sbon_non_const_size(local, sizeof(int)); + + // False positive + // The analysis doesn't know that `size` is `sizeof(int)` + size_t size2 = sizeof(int); + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + const size_t size3 = sizeof(int); + // The analysis knows that `size3` is `sizeof(int)` so it doesn't warn in + // this case. + void* __sized_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is `sizeof(int)` + *size = sizeof(int); + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + + +// trap +void* __sized_by(*size) use_sb_non_const_sizeof_int_plus_one_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(size)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(5UL)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sb_non_const_size(local, sizeof(int)+1); + + // The analysis isn't aware that `size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + size_t size2 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void* __sized_by(size2) local2 = local; + + // The analysis is aware that `size` is `sizeof(int)+1` so the warning is + // more specific. + const size_t size3 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void* __sized_by(size3) local3 = local; + + // The analysis isn't aware that `*size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + *size = sizeof(int)+1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// traps iff ptr is not null +void* __sized_by_or_null(*size) use_sbon_non_const_sizeof_int_plus_one_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sbon_non_const_size(local, sizeof(int)+1); + + // The analysis isn't aware that `size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + size_t size2 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + // The analysis is aware that `size` is `sizeof(int)+1` so the warning is + // more specific. + const size_t size3 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void* __sized_by_or_null(size3) local3 = local; + + // The analysis isn't aware that `*size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + *size = sizeof(int)+1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + +//============================================================================== +// Multiple __single assignments +// +// This is non-exhaustive due to the huge number of test cases we'd need to +// write. +//============================================================================== + +int* single_global; // expected-note{{pointer 'single_global' declared here}} +struct StructWithSinglePtr { + int* ptr; // expected-note{{StructWithSinglePtr::ptr declared here}} + int* arr[4]; // expected-note{{StructWithSinglePtr::arr declared here}} +}; +int* ret_single(); // expected-note{{'ret_single' declared here}} + +void cb_test_multiple_single_assignees_same_size( + int* p, // expected-note{{pointer 'p' declared here}} + struct StructWithSinglePtr s, + size_t size) { + int* single_ptrs[4] = {0}; // expected-note{{single_ptrs' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + + // expected-note@+1{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = p; + // expected-note@+1{{__single struct member 'ptr' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = s.ptr; + // expected-note@+1{{__single global 'single_global' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = single_global; + // expected-note@+1{{__single element from array 'single_ptrs' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = single_ptrs[0]; + // expected-note@+1{{__single return value from call to 'ret_single' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = ret_single(); + // FIXME: This text is slightly wrong. `arr` isn't being assigned directly. + // expected-note@+1{{__single struct member 'arr' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes}} + local = s.arr[0]; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size(local, size); +} + +_Static_assert(sizeof(long long int) > sizeof(int), "unexpected size diff"); +void consume_one_ll_int(long long int* __counted_by(1)); +void cb_test_multiple_single_assignees_mixed_sizes_suppresses_warning(int* p, long long int* q, int cond) { + long long int* local; + if (cond) + local = q; + else + local = (long long int* __bidi_indexable)(int* __bidi_indexable) p; // out-of-bounds pointer + + // False negative: The analysis conservatively assumes that `local` takes the + // largest bounds. In this case the assignment of `q` has the largest bounds + // which would not lead to a trap. The assignment of `p` would be a trap + consume_one_ll_int(local); +} + +void consume_dynamic_ll_int(long long int* __counted_by(size), size_t size); +void cb_test_multiple_single_assignees_mixed_sizes_warns( + char* p, // expected-note 2{{pointer 'p' declared here}} + int* q, // expected-note 2{{pointer 'q' declared here}} + int cond, size_t size) { + long long int* local; // expected-note 2{{pointer 'local' declared here}} + if (cond) { + // expected-note@+2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but 'local' has pointee type 'long long' (8 bytes)}} + // expected-note@+1{{_single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = (long long int* __bidi_indexable)(int* __bidi_indexable) q; // out-of-bounds pointer + } + else { + // expected-note@+2{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'char' (1 bytes) but 'local' has pointee type 'long long' (8 bytes)}} + // expected-note@+1{{_single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'char' (1 bytes)}} + local = (long long int* __bidi_indexable)(char* __bidi_indexable) p; // out-of-bounds pointer + } + + // expected-warning@+2{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'long long'}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + consume_dynamic_ll_int(local, size); +} + +//============================================================================== +// __unsafe_forge_bidi_indexable +//============================================================================== + +void consume_sized_by_void(void* __sized_by(size), size_t size); +void void_bidi(void * p) { + // False negative: This will trap. The analysis currently doesn't + // understand pointers that come from `__unsafe_forge_bidi_indexable`. + // + // This isn't really `__single` so it's not surprising the analysis does + // not understand this. + void* local = __unsafe_forge_bidi_indexable(void*, p, sizeof(char)); + consume_sized_by_void(local, sizeof(int)); +} + + diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c new file mode 100644 index 0000000000000..9ea15034e9e31 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c @@ -0,0 +1,334 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +typedef int SmallTy; +typedef long long int BigTy; +_Static_assert(sizeof(BigTy) > sizeof(SmallTy), "expected size diff failed"); + + +//============================================================================== +// Explicit Unsafe casts that stay as __bidi_indexable pointers +//============================================================================== +void unsafe_explicit_cast_assign_to_local(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_assign_to_local_multiple_singles( + SmallTy* p, // expected-note 2{{pointer 'p' declared here}} + SmallTy* q, // expected-note 2{{pointer 'q' declared here}} + int condition) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + if (condition) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = q; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +struct StructWithBigTyBidi { + BigTy* __bidi_indexable field; +}; + + +struct NestedStructWithBigTy { + struct StructWithBigTyBidi nested; + BigTy* __bidi_indexable field; +}; + +void unsafe_explicit_cast_struct_init(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + struct StructWithBigTyBidi local2 = {.field = (BigTy*) local}; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .field = (BigTy*) local}; +} + +BigTy* __bidi_indexable unsafe_explicit_return_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (BigTy*) local; +} + +void receive_bidi(BigTy* __bidi_indexable); +void unsafe_explicit_cast_call_arg(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + receive_bidi((BigTy*) local); +} + +void unsafe_explicit_cast_in_expr(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+3 2{{pointer 'local' declared here}} + // expected-note@+2{{_single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but '((BigTy *)local)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in '((BigTy *)local)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*) local)[0] = 0; +} + +typedef char TinyTy; +_Static_assert(sizeof(SmallTy) > sizeof(TinyTy), "expected size diff failed"); + +void unsafe_explicit_cast_assign_to_local_zero_elt_oob(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_struct_init_zero_elt_oob(TinyTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + + struct StructWithBigTyBidi local2 = { + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .field = (BigTy*) local + }; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .field = (BigTy*) local}; +} + +//============================================================================== +// Implicit Unsafe casts that stay as __bidi_indexable pointers +//============================================================================== +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void unsafe_implicit_cast_assign_to_local(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = local; + + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = local; +} + +void unsafe_implicit_cast_struct_init(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + struct StructWithBigTyBidi local2 = {.field = local}; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .nested = {.field = local}, + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .field = local}; +} + +BigTy* __bidi_indexable unsafe_implicit_return_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return local; +} + +void unsafe_implicit_cast_call_arg_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + receive_bidi(local); +} + +void unsafe_implicit_cast_assign_to_local_zero_elt_oob(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but 'local' has pointee type 'SmallTy' (aka 'int') (4 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = local; + + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = local; +} + +#pragma clang diagnostic pop + +//============================================================================== +// Explicit Unsafe casts that then are later converted to __single pointers +//============================================================================== +void unsafe_explicit_cast_assign_to_local_single(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = (BigTy*) local; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = (BigTy*) local; +} + +struct StructWithBigTySingle { + BigTy* field; +}; + + +struct NestedStructWithBigTySingle { + struct StructWithBigTySingle nested; + BigTy* field; +}; + +void unsafe_explicit_cast_struct_init_single(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local2 = {.field = (BigTy*) local}; + + struct NestedStructWithBigTySingle local3 = { + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .field = (BigTy*) local}; +} + +BigTy* unsafe_explicit_return_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + return (BigTy*) local; +} + +void receive_single(BigTy*); +void unsafe_explicit_cast_call_arg_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + receive_single((BigTy*) local); +} + +void unsafe_explicit_cast_assign_to_local_zero_elt_oob_single(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = (BigTy*) local; + + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_in_expr_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + ((BigTy* __single) local)[0] = 5; +} + +//============================================================================== +// Implicit unsafe casts that then are later converted to __single pointers +//============================================================================== +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void unsafe_implicit_cast_assign_to_local_single(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = local; +} + +void unsafe_implicit_cast_struct_init_single(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local2 = {.field = local}; + + struct NestedStructWithBigTySingle local3 = { + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .nested = {.field = local}, + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .field = local}; +} + +BigTy* unsafe_implicit_return_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + return local; +} + +void unsafe_implicit_cast_call_arg_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + receive_single(local); +} + +void unsafe_implicit_cast_assign_to_local_zero_elt_oob_single(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but 'local' has pointee type 'SmallTy' (aka 'int') (4 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = local; +} + +#pragma clang diagnostic pop diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c new file mode 100644 index 0000000000000..578f0440c374c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c @@ -0,0 +1,427 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +typedef int SmallTy; +typedef long long int BigTy; +_Static_assert(sizeof(BigTy) > sizeof(SmallTy), "expected size diff failed"); + +//============================================================================== +// Local __bidi_indexable with bounds smaller than its element type (i.e. the +// 0th element can't be accessed safely). +// +// Currently we don't generate traps for this (rdar://119744147) but we still +// warn because it will become a trap in the future. +//============================================================================== + +void param_single_explicit_cast_deref(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + // expected-warning@+1{{dereferencing __bidi_indexable 'local' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + *local = 0; +} + +void param_single_explicit_cast_deref_with_cast_to_larger(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+3 2{{pointer 'local' declared here}} + // expected-note@+2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but '((BigTy *)local)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+2{{dereferencing __bidi_indexable '((BigTy *)local)' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in '((BigTy *)local)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local) = 0; +} + + +void param_single_explicit_cast_index(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + // expected-warning@+1{{indexing __bidi_indexable 'local' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + local[0] = 0; +} + +struct SmallStructTy { + int a; + int b; +}; +_Static_assert(sizeof(struct SmallStructTy) == 8, "wrong size"); + +struct BigStructTy { + struct SmallStructTy inner; + int c; +}; +_Static_assert(sizeof(struct BigStructTy) == 12, "wrong size"); +_Static_assert(sizeof(struct SmallStructTy) < sizeof(struct BigStructTy), "expected size diff failed"); + +void param_single_explicit_cast_struct_deref(struct SmallStructTy* p, struct BigStructTy* q) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructTy' (12 bytes)}} + struct BigStructTy* local = (struct BigStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + // expected-warning@+1{{dereferencing __bidi_indexable 'local' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + *local = *q; +} + +struct StructWithBigTyBidi { + BigTy* __bidi_indexable member; +}; + +void receiveBigTyBidi(BigTy* __bidi_indexable); +BigTy* __bidi_indexable param_single_explicit_cast_oob_escapes_bidi(SmallTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+2 4{{pointer 'local' declared here}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* local2 = local; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTyBidi local3 = { .member = local }; + + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTyBidi(local); + + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +// Test that multiple single 'entities' are supported. This is in no way +// exhaustive. + +SmallTy* retSmallTy(void); // expected-note 4{{'retSmallTy' declared here}} +struct StructWithSmallTySingle { + SmallTy* member; // expected-note 4{{StructWithSmallTySingle::member declared here}} +}; +SmallTy* globalSmallTy; // expected-note 4{{pointer 'globalSmallTy' declared here}} +BigTy* __bidi_indexable param_single_explicit_cast_oob_escapes_bidi_multiple_singles( + SmallTy* p, // expected-note 4{{pointer 'p' declared here}} + int condition, + struct StructWithSmallTySingle s) { + SmallTy* arr[2]; // expected-note 4{{'arr' declared here}} + // expected-note@+1 4{{pointer 'local' declared here}} + BigTy* local; + if (condition == 0) + // expected-note@+1 4{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + else if (condition == 1) + // expected-note@+1 4{{__single global 'globalSmallTy' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) globalSmallTy; + else if (condition == 2) + // expected-note@+1 4{{__single return value from call to 'retSmallTy' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) retSmallTy(); + else if (condition == 3) + // expected-note@+1 4{{__single struct member 'member' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) s.member; + else if (condition == 4) + // expected-note@+1 4{{_single element from array 'arr' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) arr[0]; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* local2 = local; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTyBidi local3 = { .member = local }; + + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTyBidi(local); + + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +struct StructWithBigTySingle { + BigTy* member; +}; + +void receiveBigTySingle(BigTy*); +BigTy* param_single_explicit_cast_oob_escapes_single(SmallTy* p) { // expected-note 8{{pointer 'p' declared here}} + // expected-note@+3 8{{pointer 'local' declared here}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local3 = { .member = local }; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTySingle(local); + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +// TODO: add __single variant + +//============================================================================== +// Member access +// +// Despite the bounds of the local (that suggest some part of it can +// be accessed) access through `->` will trap if **any** field is accessed +// due to `CodeGenFunction::EmitMemberExpr` taking the size of the base expr +// to `->` into account. +//============================================================================== + +void param_single_explicit_cast_nested_member_access(struct SmallStructTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructTy' (12 bytes)}} + struct BigStructTy* local = (struct BigStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructTy::c through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->c = 0; +} + +void param_single_nested_member_access_explicit_cast_to_larger_at_use(struct SmallStructTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but cast of 'local' to 'struct BigStructTy *__bidi_indexable' has pointee type 'struct BigStructTy' (12 bytes)}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but '((struct BigStructTy *)local)' has pointee type 'struct BigStructTy' (12 bytes}} + struct SmallStructTy* local = p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `((BigStructTy*)local)->` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->inner.a = 0; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->inner.b = 0; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::c through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->c = 0; +} + +void param_single_nested_member_access_explicit_cast_to_smaller_at_use(struct BigStructTy* p) { + struct BigStructTy* local = p; + + // No warnings when casting to a smaller type. + ((struct SmallStructTy*) local)->a = 0; + ((struct SmallStructTy*) local)->b = 0; +} + +struct BigStructWithFAMTy { + struct SmallStructTy inner; + int c; + char buf[__counted_by(c)]; +}; + +void param_single_explicit_cast_fam_member_access(struct SmallStructTy* p) { // expected-note 5{{pointer 'p' declared here}} + // expected-note@+2 5{{pointer 'local' declared here}} + // expected-note@+1 5{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithFAMTy' (12 bytes)}} + struct BigStructWithFAMTy* local = (struct BigStructWithFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->buf[0] = 0; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + char* local2 = local->buf + 1; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + char* local3 = &(local->buf[0]); +} + +struct BigStructWithBitFieldsTy { + struct SmallStructTy inner; + int c:1; + int d:1; +}; + +void param_single_explicit_cast_bit_field_access(struct SmallStructTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+2 4{{pointer 'local' declared here}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithBitFieldsTy' (12 bytes)}} + struct BigStructWithBitFieldsTy* local = (struct BigStructWithBitFieldsTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::c through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->c = 0; + + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::d through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->d = 0; +} + +struct BigStructWithUnannotatedFAMTy { + struct SmallStructTy inner; + int c; + char buffer[]; +}; + +void param_single_explicit_cast_unannotated_fam_member_access(struct SmallStructTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithUnannotatedFAMTy' (12 bytes)}} + struct BigStructWithUnannotatedFAMTy* local = (struct BigStructWithUnannotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithUnannotatedFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithUnannotatedFAMTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithUnannotatedFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithUnannotatedFAMTy'}} + local->inner.b = 0; + + // The warning is suppressed in this case because `warn_bounds_safety_promoting_incomplete_array_without_count` already fires. + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + local->buffer[0] = 0; +} + +union BigUnionTy { + struct SmallStructTy inner; + struct BigStructWithFAMTy big_inner; +}; + +void param_single_explicit_cast_union_access(struct SmallStructTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+2 6{{pointer 'local' declared here}} + // expected-note@+1 6{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'union BigUnionTy' (12 bytes)}} + union BigUnionTy* local = (union BigUnionTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // expected-warning@+1{{accessing field BigUnionTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigUnionTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->inner.b = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.inner.a = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.inner.b = 0; + + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.c = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.buf[0] = 0; +} + +//============================================================================== +// False negatives +//============================================================================== + +void param_single_explicit_cast_not_all_assignments_too_small(SmallTy* p) { + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + *local = 0; // No warn + + // Currently happens-before isn't taken into account so this assignment is + // considered which means not all assignments to `local` are a __single pointer + // that has element type which is too small. + local = 0; +} + +void param_single_explicit_cast_not_all_assignments_too_small2(SmallTy* p, BigTy* q, int condition) { + BigTy* local; + if (condition) + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + else + local = q; + + *local = 0; // No warn +} + +struct OtherStructWithAnnotatedFAMTy { + int a; + int b; + char buffer[__counted_by(a)]; +}; +_Static_assert(sizeof(struct OtherStructWithAnnotatedFAMTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_annotated_fam_member_access_false_neg(struct SmallStructTy* p) { + struct OtherStructWithAnnotatedFAMTy* local = (struct OtherStructWithAnnotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + local->a = 0; + local->b = 0; + + // TODO: We should warn about this because its a guaranteed runtime trap. + // rdar://120566596 + local->buffer[0] = 0; +} + +struct OtherStructWithUnannotatedFAMTy { + int a; + int b; + char buffer[]; +}; +_Static_assert(sizeof(struct OtherStructWithUnannotatedFAMTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_unannotated_fam_member_access_false_neg(struct SmallStructTy* p) { + struct OtherStructWithUnannotatedFAMTy* local = (struct OtherStructWithUnannotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + local->a = 0; + local->b = 0; + + // This existing warning already fires so this case doesn't need additional + // support but this test case is here to make sure this doesn't regress. + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + local->buffer[0] = 0; +} + +//============================================================================== +// No warnings expected +//============================================================================== + +// Same layout as `SmallStructTy` but not the same type +struct OtherSmallStructTy { + int a; + int b; +}; +_Static_assert(sizeof(struct OtherSmallStructTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_same_struct_size(struct SmallStructTy* p, struct OtherSmallStructTy* q) { + struct OtherSmallStructTy* local = (struct OtherSmallStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + *local = *q; +} + +// Flexible Array Members + +struct SmallFAM { + int count; + char buf[__counted_by(count)]; +}; +_Static_assert(sizeof(struct SmallFAM) == 4, "wrong size"); + +struct BiggerFAM { + int count; + int extra_field; + char buf[__counted_by(count)]; +}; +_Static_assert(sizeof(struct BiggerFAM) == 8, "wrong size"); + +_Static_assert(sizeof(struct SmallFAM) < sizeof(struct BiggerFAM), "expected size diff failed"); + +void param_single_explicit_cast_fam_struct_deref(struct SmallFAM* p) { + struct BiggerFAM* local = (struct BiggerFAM* __bidi_indexable)(struct SmallFAM* __bidi_indexable) p; + // No warn + // Even though `p` is a `__single` when it is assigned to `local` the bounds + // are dynamically computed from `p`'s count field. Thus the compiler can't + // know what the bounds of `local` will be at runtime. + local[0].count = 0; +} diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c new file mode 100644 index 0000000000000..31ce2832eb574 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c @@ -0,0 +1,2020 @@ + +// -triple arm64-apple-darwin23.2.0 is used because some diagnostic text mentions platform specific type sizes +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fblocks -Wno-gnu-alignof-expression -triple arm64-apple-darwin23.2.0 -verify %s + +#include + +//============================================================================== +// Parameter source of __single +//============================================================================== + +void implicit_unconditional_trap(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +// Currently we warn here but we may want to use an explicit cast as a suppression mechanism in the future. +void explicit_single_param(int * __single p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void explicit_single_param_cast(int * __single p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = (int* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +// Don't duplicate the existing warning for explicitly __bidi_indexable pointers. +void explicit_unconditional_trap(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *__bidi_indexable local = p; // expected-warning{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + for (int i = 0; i < count; ++i) + local[i] = 0; +} + +// Don't duplicate the existing warning for explicitly __bidi_indexable pointers. +void explicit_unconditional_trap_assign(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *__bidi_indexable local; + local = p; // expected-warning{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + for (int i = 0; i < count; ++i) + local[i] = 0; +} + +// The parameter is properly annotated, no need to warn. +void counted_by_param(int *__counted_by(count) p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void indexable_param(int *__indexable p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void bidi_indexable_param(int *__bidi_indexable p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void null_terminated_param(int *__null_terminated p) { + int *local = __unsafe_null_terminated_to_indexable(p); + while (local) { + *local = 1; + ++local; // no-warning + } +} + +void implicit_unconditional_trap_Nonnull_attr(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int * _Nonnull local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void simple_assignment(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *local; // expected-note{{pointer 'local' declared here}} + local = p; // expected-note{{pointer 'local' assigned here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void reassignment_after_indexing(int *p, int count) { // expected-note{{pointer 'p' declared here}} + // expected-note@+1{{__single parameter 'p' assigned to 'local' here}} + int *local = p; // expected-note{{pointer 'local' declared here}} + int *__single something_else; // expected-note{{pointer 'something_else' declared here}} + for (int i = 0; i < count; ++i) { + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 1 or < 0}} + local[i] = 0; + local = something_else; // expected-note{{__single local variable 'something_else' assigned to 'local' here}} + } +} + +void reassignment_after_indexing_no_warn(int *p, int count) { + int *local = p; // no-warning + int *__bidi_indexable something_else; + for (int i = 0; i < count; ++i) { + local[i] = 0; + local = something_else; // This value isn't a __single + } +} + +void unary_op(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + ++local; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op2(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + --local; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op3(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local++; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op4(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local--; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + + +void binary_op(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(local + 2) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op2(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(2 + local) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op3(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + int* new_p = local + 2; +} + +void binary_op4(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(local - 1) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + + +void binary_op_non_constant_offset(int *p, int offset) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 1 or < 0}} + int* new_p = local + offset; +} + +void binary_op_constant_offset(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; + int* new_p; + new_p = local + 0; // No warning + new_p = 0 + local; // No warning + + int *local2 = p; // expected-note{{pointer 'local2' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + new_p = local2 + 1; +} + + +void binary_op_compound_constant_offset(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local += 2; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op_compound_variable_index(int *p, int offset) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local += offset; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 1 or < 0}} +} + +void constant_index(int* p) { // expected-note 2 {{pointer 'p' declared here}} + int* local = p; // expected-note 2 {{pointer 'local' initialized here}} + local[0] = 5; // No warning + local[(int) 0] = 6; // No warning + local[((int) 0)] = 6; // No warning + local[1+2+3 - 6] = 6; // No warning + *local = 10; // No warning + + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 6; + + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1+2+3 -5] = 6; +} + +void param_single_paren_used_initialized_arith(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local) = (p); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + (++local); +} + +void param_single_paren_used_initialized_arith2(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local) = (p); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + (local) += 1; +} + +void param_single_paren_used_assign_arith(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + (++local); +} + +void param_single_paren_used_assign_idx(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = ((int*) p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast2(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (int*) (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast3(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = ((int*) ((int*) p)); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +struct UnsizedType; +struct SizedType { + int foo; +}; + +void warn_param_incomplete_type_to_sized(struct UnsizedType* p) { // expected-note{{pointer 'p' declared here}} + struct SizedType* local = (struct SizedType*) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].foo = 0; +} + +void param_single_explicit_bidi_cast(int* p) { // expected-note{{pointer 'p' declared here}} + int* local = (int* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +typedef long long int BigTy; +typedef int SmallerTy; + +_Static_assert(sizeof(BigTy) > sizeof(SmallerTy), "expected size diff failed"); +void param_single_explicit_cast_type_change_second_cast_to_bigger(BigTy* p) { + // We should not warn here because `local` gets the bounds of a single + // `BigTy`, not the bounds of a single `SmallerTy`. This is because + // the BoundsSafetyPointerCast will create the bounds from BigTy and + // then the BitCast (which does not affect the stored bounds) to SmallerTy + // happens afterwards. This can be seen from the AST: + // + // + // `-CStyleCastExpr 'SmallerTy *__bidi_indexable' + // `-CStyleCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'BigTy *__single' part_of_explicit_cast + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + // + // if sizeof(BigTy) == 8 and sizeof(SmallerTy) == 4, then its safe to index + // local[0] and local[1] + // + SmallerTy* local = (SmallerTy* __bidi_indexable)(BigTy* __bidi_indexable) p; + local[1] = 0; // No warn +} + +void param_single_explicit_cast_type_change_second_cast_to_smaller(SmallerTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes}} + BigTy* local = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + // expected-warning@+1{{indexing __bidi_indexable 'local' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + local[1] = 0; +} + +void param_single_explicit_cast_to_larger_and_attr_change(SmallerTy* p) { // expected-note{{pointer 'p' declared her}} + // This example traps at runtime because the `local` gets the bounds of a + // single `BigTy`. + // + // `-CStyleCastExpr 0x12e02a528 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 0x12e02a510 'BigTy *__single' part_of_explicit_cast + // `-ImplicitCastExpr 0x12e02a498 'SmallerTy *__single' part_of_explicit_cast + // `-DeclRefExpr 0x12e02a460 'SmallerTy *__single' lvalue ParmVar 0x12d90b008 'p' 'SmallerTy *__single' + // + BigTy* local = (BigTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-single-to-indexable-bounds-truncated" +void param_single_explicit_cast_to_smaller_and_attr_change(BigTy* p) { // expected-note{{pointer 'p' declared her}} + // This example traps at runtime because the `(SmallerTy* __bidi_indexable)` explicit + // cast does the BitCast first, then the BoundsSafetyPointerCast. So `local` gets + // the bounds of a single `SmallerTy`. + // + // `-CStyleCastExpr 'int *__bidi_indexable' + // `-ImplicitCastExpr 'int *__single' part_of_explicit_cast + // `-ImplicitCastExpr 'long long *__single' part_of_explicit_cast + // `-DeclRefExpr 'long long *__single' lvalue ParmVar 0x135115908 'p' 'long long *__single' + // + SmallerTy* local = (SmallerTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void param_single_implicit_cast_to_smaller_and_attr_change(BigTy* p) { // expected-note{{pointer 'p' declared here}} + // This example traps the BitCast happens before the BoundsSafetyPointerCast so + // the wide pointer gets the bounds of a single `SmallerTy`. + // + // `-ImplicitCastExpr 'SmallerTy *__bidi_indexable' + // `-ImplicitCastExpr 'int *__single' + // `-ImplicitCastExpr 'BigTy *__single' + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + SmallerTy* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_implicit_cast_to_larger_and_attr_change(SmallerTy* p) { // expected-note{{pointer 'p' declared here}} + // This example traps the BitCast happens before the BoundsSafetyPointerCast so + // the wide pointer gets the bounds of a single `BigTy`. + // + // `-VarDecl col:10 used local 'BigTy *__bidi_indexable' cinit + // `-ImplicitCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'long long *__single' + // `-ImplicitCastExpr 'SmallerTy *__single' + // `-DeclRefExpr 'SmallerTy *__single' lvalue ParmVar 'p' 'SmallerTy *__single' + // + BigTy* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +struct SimpleType { + int member; +}; + +struct SameLayoutAsSimpleType { + int member; +}; +_Static_assert(sizeof(struct SimpleType) == sizeof(struct SameLayoutAsSimpleType), "Expected types to have same size"); + +void param_implicit_cast_to_type_with_same_layout(struct SimpleType* p) { // expected-note{{pointer 'p' declared here}} + // Check that even though SimpleType and SameLayoutAsSimpleType are not the same + // that a warning is still emitted. + // + struct SameLayoutAsSimpleType* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].member = 0; +}; + +void param_explicit_cast_to_type_with_same_layout(struct SimpleType* p) { // expected-note{{pointer 'p' declared here}} + // Check that even though SimpleType and SameLayoutAsSimpleType are not the same + // that a warning is still emitted. + // + struct SameLayoutAsSimpleType* local = (struct SameLayoutAsSimpleType*) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].member = 0; +}; + + +#pragma clang diagnostic pop +#pragma clang diagnostic pop + +void param_multiple_fb_cast(BigTy* p) { // expected-note{{pointer 'p' declared here}} + // Check we still warn when there are multiple BoundsSafetyPointerCast. + // + // This example traps because only the top BoundsSafetyPointerCast matters. + // `local` gets the bounds of a single `SmallerTy`. + // + // `-CStyleCastExpr 'SmallerTy *__bidi_indexable' + // `-CStyleCastExpr 'SmallerTy *__single' + // `-ImplicitCastExpr 'SmallerTy *__bidi_indexable' part_of_explicit_cast + // `-CStyleCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'BigTy *__single' part_of_explicit_cast + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + SmallerTy* local = (SmallerTy* __bidi_indexable)(SmallerTy* __single) (BigTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + + +// expected-note@+2{{pointer 'p' declared here}} +// expected-note@+1{{pointer 'q' declared here}} +void param_multiple_singles_idx(int* p, int* q) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +// expected-note@+2{{pointer 'p' declared here}} +// expected-note@+1{{pointer 'q' declared here}} +void param_multiple_singles_arith(int* p, int* q) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +void param_multiple_assignments_with_branches(int b, + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{pointer 'q' declared here}} + int* q) { + + int* local; // expected-note{{pointer 'local' declared here}} + if (b) + local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + else + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Pointer arithmetic with mismatched pointees +//============================================================================== +typedef char EvenSmallerTy; +_Static_assert(sizeof(EvenSmallerTy) < sizeof(SmallerTy), "unexpected type size diff"); +void param_binary_mismatched_pointee_signed_variable_offset(SmallerTy* p, int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining what values of `offset` create an out-of-bounds pointer. + // + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_signed_variable_offset_multiple_single_entities( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, int off) { + + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 6{{pointer 'local' declared here}} + SmallerTy* local = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 4{{__single parameter 'q' assigned to 'local' here}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining what values of `offset` create an out-of-bounds pointer. + // + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_unsigned_variable_offset(SmallerTy* p, unsigned int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + + // Warn even though this could be a false positive because we don't know the + // value of `offset` at compile time. The warning tries to workaround that by + // explaining which values of `offset` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4{{$}}}} + *((EvenSmallerTy*)local + off) = 0; + + // Warn even though this could be a false positive when `offset == 0`. + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is < 0{{$}}}}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_unsigned_variable_offset_multiple_single_entities( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, unsigned int off) { + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 6{{pointer 'local' declared here}} + SmallerTy* local = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+1 4{{_single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining which values of `offset` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4{{$}}}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is < 0{{$}}}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_const_offset(SmallerTy* p) { // expected-note 12{{pointer 'p' declared here}} + // expected-note@+3 8{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + const int signedOne = 1; + const int signedFour = 4; + + // Positive or zero effective offsets that won't generate an out-of-bounds pointer + _Static_assert(sizeof(SmallerTy) == 4, "unexpected size"); + *((EvenSmallerTy*)local + 0) = 0; + *((EvenSmallerTy*)local + 1) = 0; + *((EvenSmallerTy*)local + 2) = 0; + *((EvenSmallerTy*)local + 3) = 0; + *((EvenSmallerTy*)local + signedOne) = 0; + + *((EvenSmallerTy*)local - -0) = 0; + *((EvenSmallerTy*)local - -1) = 0; + *((EvenSmallerTy*)local - -2) = 0; + *((EvenSmallerTy*)local - -3) = 0; + + // Positive effective offsets that will generate an out-of-bounds pointer + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + 4) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - -4) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + signedFour) = 0; + + // Negative effective offsets that will generate an out-of-bounds pointer + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local -1) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + -1) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - signedOne) = 0; + + // Large negative constant + const long long int MostNegativeValue = 0x8000000000000000UL; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + MostNegativeValue) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - MostNegativeValue) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + 0) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - 0) = 0; +} + +void param_unary_mismatched_pointee(SmallerTy* p) { // expected-note 4{{pointer 'p' declared here}} + SmallerTy* local = p; + + // These constructs are forbidden. They are commented out because their + // presence seems to prevent emission of subsequent diagnostics. + // error: assignment to cast is illegal, lvalue casts are not supported + // ((char*) local)++; + // ++((char*) local); + // ((char*) local)--; + // --((char*) local); + // ((char*) local) += 1; + // ((char*) local) -= 1; + + // The pointee type of the `p` and `local2` do not match + // sizeof(*p) > sizeof(*local2) + EvenSmallerTy* local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + // Don't warn. A single increment won't go out-of-bounds. + ++local2; + --local2; + local2++; + local2--; + + // There's a false negative here. The analysis isn't tracking the value + // of pointers as execution progresses so there's no way to detect this right + // now. + ++local2; + ++local2; + ++local2; // `local2` is now out-of-bounds here. + + // The pointee type of the `p` and `local3` do not match + // sizeof(*p) < sizeof(*local3). So this pointer points to partially + // out-of-bounds memory. + // + // expected-note@+1 4{{pointer 'local3' initialized here}} + BigTy* local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + ++local3; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + --local3; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + local3++; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + local3--; +} + +void param_unary_mismatched_pointee_multiple_single_entities(SmallerTy* p, // expected-note 4{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 4{{pointer 'q' declared here}} + int condition) { + SmallerTy* local = p; + + if (condition) + local = q; + + // These constructs are forbidden. They are commented out because their + // presence seems to prevent emission of subsequent diagnostics. + // error: assignment to cast is illegal, lvalue casts are not supported + // ((char*) local)++; + // ++((char*) local); + // ((char*) local)--; + // --((char*) local); + // ((char*) local) += 1; + // ((char*) local) -= 1; + + // The pointee type of the `p` and `local2` do not match + // sizeof(*p) > sizeof(*local2) + EvenSmallerTy* local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + if (condition) + local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) q; + + // Don't warn. A single increment won't go out-of-bounds. + ++local2; + --local2; + local2++; + local2--; + + // There's a false negative here. The analysis isn't tracking the value + // of pointers as execution progresses so there's no way to detect this right + // now. + ++local2; + ++local2; + ++local2; // `local2` is now out-of-bounds here. + + // The pointee type of the `p` and `local3` do not match + // sizeof(*p) < sizeof(*local3). So this pointer points to partially + // out-of-bounds memory. + // + // expected-note@+2 4{{pointer 'local3' declared here}} + // expected-note@+1 4{{__single parameter 'p' assigned to 'local3' here}} + BigTy* local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + if (condition) + // expected-note@+1 4{{__single parameter 'q' assigned to 'local3' here}} + local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) q; + + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + ++local3; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + --local3; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + local3++; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + local3--; +} + + +// Original reproducer for rdar://122055103 +typedef struct Header { + int count; +} Header_t; +typedef struct Nested { + Header_t header; + int more_data; +} Nested_t; + +void my_memset(void*__sized_by(size), int value, unsigned long long size); + +void test(Nested_t* p) { + Nested_t* local = p; + // There should be no warning here. This pointer arithmetic is in-bounds. + my_memset((char*) local + sizeof(Header_t), 0, sizeof(Nested_t) - sizeof(Header_t)); +} + +//============================================================================== +// Indexing with mismatched pointees +//============================================================================== +typedef char EvenSmallerTy; +_Static_assert(sizeof(EvenSmallerTy) < sizeof(SmallerTy), "unexpected type size diff"); +void param_idx_mismatched_pointee_signed_variable_offset(SmallerTy* p, int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + + +void param_idx_mismatched_pointee_signed_variable_offset_multiple_assignees( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, + int off) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + if (cond) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local2 = q; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_unsigned_variable_offset(SmallerTy* p, unsigned off, unsigned long long off2) { // expected-note 7{{pointer 'p' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[off] = 0; + + // The warning text is a little surprising at first glance. One might expect + // it to say `if the index expression is < 0`. However, the result of + // negating an unsigned type is still an unsigned type so the warning only + // says `>= 4`. So while the programmer probably intended a negative offset + // they will likely get a larger offset than intended. + // + // For the first case `-off` will get zero extended after being negated which + // can result in a large offset. E.g. if `off` is `1` `-off` becomes 2**32 -1 + // (`-1` ins 32-bit two's complement). + // + // For the second case `-off` will not get extended after being negated. This + // can result in an even larger offset. E.g. if `off` is `1` `-off` becomes + // 2**64 -1 (`-1` in 64-bit two's complement) + + // Ideally clang should warn about code like this but it currently doesn't + // (rdar://123416393). + // + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off2] = 0; + + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_unsigned_variable_offset_multiple_assignees( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, + unsigned off) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + if (cond) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes}} + SmallerTy* local2 = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local2 = q; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_const_offset(SmallerTy* p) { // expected-note 10{{pointer 'p' declared here}} + // expected-note@+1 6{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + const int signedOne = 1; + const int signedFour = 4; + + // Positive or zero effective offsets that won't generate an out-of-bounds pointer + _Static_assert(sizeof(SmallerTy) == 4, "unexpected size"); + ((EvenSmallerTy*)local)[0] = 0; + ((EvenSmallerTy*)local)[1] = 0; + ((EvenSmallerTy*)local)[2] = 0; + ((EvenSmallerTy*)local)[3] = 0; + ((EvenSmallerTy*)local)[signedOne] = 0; + ((EvenSmallerTy*)local)[-0] = 0; + + // Positive effective offsets that will generate an out-of-bounds pointer + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[4] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[signedFour] = 0; + + // Negative effective offsets that will generate an out-of-bounds pointer + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[-1] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[-signedOne] = 0; + + // Large negative constant + const long long int MostNegativeValue = 0x8000000000000000UL; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[MostNegativeValue] = 0; + // expected-warning-re@+3{{overflow in expression; result is {{.+}} with type 'long long'}} + // expected-warning@+2{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + // Note: When overflow happens Clang fails to evaluate the index expression as a constant + ((EvenSmallerTy*)local)[-MostNegativeValue] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*)local2)[0] = 0; + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*)local2)[-0] = 0; +} + +//============================================================================== +// Dereferencing with mismatched pointees +//============================================================================== + +void param_deref_mismatched_pointee_smaller(SmallerTy* p, int off) { // expected-note 2{{pointer 'p' declared here}} + SmallerTy* local = p; + *((EvenSmallerTy* __single) local) = 0; + *((EvenSmallerTy*) local) = 0; + + // Check that we don't emit any warnings about the dereferencing + *(((EvenSmallerTy*) local) + 0) = 0; + *(((EvenSmallerTy*) local) + 1) = 0; + *(((EvenSmallerTy*) local) + 2) = 0; + *(((EvenSmallerTy*) local) + 3) = 0; + + // expected-note@+1{{_single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local2 = p; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *(((EvenSmallerTy*) local2) + 4) = 0; + + // expected-note@+1{{__single parameter 'p' used to initialize 'local3' here results in 'local3' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local3' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local3 = p; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *(((EvenSmallerTy*) local3) + off) = 0; +} + +void param_deref_mismatched_pointee_larger(SmallerTy* p, int off) { // expected-note 7{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local = p; + // No warning about dereference as its dereferencing a __single. + // expected-warning@+1{{explicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + *((BigTy* __single) local) = 0; + + // expected-note@+3 2{{pointer 'local2' declared here}} + // expected-note@+2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + // expected-warning@+2{{dereferencing __bidi_indexable '((BigTy *)local2)' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + *((BigTy*) local2) = 0; + + // expected-note@+2{{pointer 'local3' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local3' here results in 'local3' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local3' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local3 = p; + // No warning about dereference: There's no warning because the current + // implementation only warns if the operand of the dereference is a DeclRefExpr + // after walking through all the casts. This behavior is probably fine because + // there are already multiple warnings about the bad use of `local3`. + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local3' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *(((BigTy*) local3) + 0) = 0; + + // expected-note@+2{{pointer 'local4' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local4' here results in 'local4' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local4' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local4 = p; + // No warning about dereference: There's no warning because the current + // implementation only warns if the operand of the dereference is a DeclRefExpr + // after walking through all the casts. This behavior is probably fine because + // there are already multiple warnings about the bad use of `local4` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local4' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local4' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *(((BigTy*) local4) + off) = 0; +} + +//============================================================================== +// Unevaluated contexts +//============================================================================== + +int* __bidi_indexable bidi_source(void); + +void unevaluated_context_sizeof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = sizeof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `sizeof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = sizeof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context_alignof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = _Alignof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `_Alignof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = _Alignof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context___alignof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = __alignof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `__alignof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = __alignof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context_typeof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + typeof(local[1]) y = 0; + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `__typeof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + typeof(local2 = bidi_source()) tmp = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context__Generic(int *p) { // expected-note 3{{pointer 'p' declared here}} + // No warning should fire here + int* local = p; + int tmp = _Generic(local[1], // Not evaluated + int:5, // Only this expr should be evaluated + char: local[1], // Not evaluated + default: local[1] // Not evaluated + ); + + // A warning should fire here + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp2 = _Generic(local2[1], // Not evaluated + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int: local2[1], // Only this expr should be evaluated + char: 5, // Not evaluated + default: 5); // Not evaluated + + // A warning should fire here + int* local3 = p; // expected-note{{pointer 'local3' initialized here}} + int tmp3 = _Generic(local2[1], // Not evaluated + char: 5, // Not evaluated + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in a trap{{$}}}} + default: local3[1]); // Only this expr should be evaluated + + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local4` in `__Generic` is ignored then the analysis only sees one assignment + // to `local4` which allows the warning at `loca42[1]` to fire. If the assignment to `local4` is not ignored + // then the warning won't fire. + int* local4 = p; // expected-note{{pointer 'local4' initialized here}} + int tmp4; + _Generic(5, + int: tmp4 = 0, + default: local4 = bidi_source() + ); + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local4' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local4[1]; +} + +//============================================================================== +// Blocks +//============================================================================== + +// In some senses this is a false positive because the block is never called. +void decl_block(void) { + int (^blk)(int*) = ^(int* x) { // expected-note{{pointer 'x' declared here}} + int* block_local = x; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'x' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void decl_block_with_call(void) { + int* __single p = 0; + int (^blk)(int*) = ^(int* x) { // expected-note{{pointer 'x' declared here}} + int* block_local = x; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'x' results in a trap{{$}}}} + return block_local[1]; + }; + blk(p); +} + +void block_capture(int* p) { // expected-note{{pointer 'p' declared here}} + int (^blk)(void) = ^(void) { + int* block_local = p; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'p' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void block_capture2(int* p) { // expected-note{{pointer 'p' declared here}} + int* __block f_local; // expected-note{{pointer 'f_local' declared here}} + int (^blk)(void) = ^(void) { + f_local = p; // expected-note{{'f_local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'f_local' assigned from __single parameter 'p' results in a trap{{$}}}} + return f_local[1]; + }; +} + +void block_capture3(void) { + int* __single p; // expected-note{{pointer 'p' declared here}} + int (^blk)(void) = ^(void) { + int* block_local = p; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single local variable 'p' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void block_local(int* p) { // expected-note{{pointer 'p' declared here}} + int* __block local; // expected-note{{pointer 'local' declared here}} + local = p; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Global source of __single +//============================================================================== + +int* global_count0; // expected-note{{pointer 'global_count0' declared here}} +int* global_count1; // expected-note{{pointer 'global_count1' declared here}} +int* global_count2; // expected-note{{pointer 'global_count2' declared here}} +int* global_count3; // expected-note{{pointer 'global_count3' declared here}} +int* global_count4; // expected-note{{pointer 'global_count4' declared here}} +void* global_count5; // expected-note{{pointer 'global_count5' declared here}} +int* global_count6; // expected-note{{pointer 'global_count6' declared here}} +int* global_count7; // expected-note{{pointer 'global_count7' declared here}} +int* global_count8; // expected-note{{pointer 'global_count8' declared here}} +int* global_count9; // expected-note{{pointer 'global_count9' declared here}} + +void initialized_from_single_global_var_idx(void) { + int* local = global_count0; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single global 'global_count0' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_single_global_var_idx(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = global_count1; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count1' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_single_global_var_arith(void) { + int* local = global_count2; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single global 'global_count2' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_single_global_var_arith(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = global_count3; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single global 'global_count3' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_single_global_var_idx_parens(void) { + int* local; // expected-note{{pointer 'local' declared here}} + (local) = (global_count4); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count4' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_single_global_var_idx_parens_cast(void) { + int* local; // expected-note{{pointer 'local' declared here}} + (local) = ((int*) global_count5); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count5' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_multiple_globals_idx(void) { + // expected-note@+1{{pointer 'local' declared here}} + int *local = global_count6; // expected-note{{__single global 'global_count6' assigned to 'local' here}} + local = global_count7; // expected-note{{__single global 'global_count7' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_globals_arith(void) { + // expected-note@+1{{pointer 'local' declared here}} + int *local = global_count8; // expected-note{{__single global 'global_count8' assigned to 'local' here}} + local = global_count9; // expected-note{{__single global 'global_count9' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +//============================================================================== +// Local var source of __single +//============================================================================== + +void initialized_from_local_single_var_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_local_single_var_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_local_single_var_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_single_var_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_local_static_single_var_idx(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_local_static_single_var_idx(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_local_static_single_var_arith(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_static_single_var_arith(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_single_var_idx_parens(int* p) { + int* __single s = (p); // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + (local) = (s); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_local_single_var_idx_parens_cast(int* p) { + void* __single s = (p); // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + (local) = ((int*) s); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + (local[1]) = 0; +} + +void initialized_from_multiple_local_single_vars_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* __single s2 = p; // expected-note{{pointer 's2' declared here}} + // expected-note@+1{{pointer 'local' declared here}} + int* local = s; // expected-note{{__single local variable 's' assigned to 'local' here}} + local = s2; // expected-note{{__single local variable 's2' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_local_single_vars_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* __single s2 = p; // expected-note{{pointer 's2' declared here}} + // expected-note@+1{{pointer 'local' declared here}} + int* local = s; // expected-note{{__single local variable 's' assigned to 'local' here}} + local = s2; // expected-note{{__single local variable 's2' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + + +//============================================================================== +// function return source of __single +//============================================================================== + +int* single_source0(void); // expected-note{{'single_source0' declared here}} +int* single_source1(void); // expected-note{{'single_source1' declared here}} +int* single_source2(void); // expected-note{{'single_source2' declared here}} +int* single_source3(void); // expected-note{{'single_source3' declared here}} +int* single_source4(void); // expected-note{{'single_source4' declared here}} +void* single_source5(void); // expected-note{{'single_source5' declared here}} +int* single_source6(void); // expected-note{{'single_source6' declared here}} +int* single_source7(void); // expected-note{{'single_source7' declared here}} +int* single_source8(void); // expected-note{{'single_source8' declared here}} +int* single_source9(void); // expected-note{{'single_source9' declared here}} + +void initialized_from_func_call_return_single_idx(void) { + int* local = single_source0(); // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source0' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_func_call_return_single_arith(void) { + int* local = single_source1(); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source1' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_func_call_return_single_idx(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = single_source2(); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single return value from call to 'single_source2' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_func_call_return_single_arith(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = single_source3(); // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single return value from call to 'single_source3' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_func_call_return_single_arith_parens(void) { + int* local = (single_source4()); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source4' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_func_call_return_single_arith_parens_cast(void) { + int* local = ((int*) single_source5()); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source5' results in an out-of-bounds pointer}} + (++local); +} + +// Currently the FunctionDecl for this function actually returns __single (but +// the return value is promoted to __bidi_indexable with the appropriate bounds +// at call sites) so special logic is used to avoid warning for functions like +// these because in practice calls to these functions are not treated as +// returning __single. +void* custom_malloc(unsigned long long size) __attribute__((alloc_size(1))); + +void initialized_from_func_call_to_alloc_func_idx(void) { + int* local = custom_malloc(5); + local[1] = 0; // No warning +} + +void initialized_from_func_call_to_alloc_func_arith(void) { + int* local = custom_malloc(5); + ++local; // No warning +} + +void assigned_from_func_call_to_alloc_func_idx(void) { + int* local; + local = custom_malloc(5); + local[1] = 0; // No warning +} + +void assigned_from_func_call_to_alloc_func_arith(void) { + int* local; + local = custom_malloc(5); + ++local; // No warning +} + +void initialized_from_multiple_func_calls_return_single_idx(void) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = single_source6(); // expected-note{{__single return value from call to 'single_source6' assigned to 'local' here}} + local = single_source7(); // expected-note{{__single return value from call to 'single_source7' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_func_calls_return_single_artih(void) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = single_source8(); // expected-note{{__single return value from call to 'single_source8' assigned to 'local' here}} + local = single_source9(); // expected-note{{__single return value from call to 'single_source9' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + + +//============================================================================== +// Array element source of __single +//============================================================================== + +void initialized_from_array_elt_idx(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ptrs[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_array_elt_arith(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ptrs[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_array_elt_idx(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local; // expected-note{{pointer 'local' declared here}} + local = ptrs[0]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single element from array 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_array_elt_arith(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local; // expected-note{{pointer 'local' declared here}} + local = ptrs[0]; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single element from array 'ptrs' results in an out-of-bounds pointer}} + ++local; +} + +int* global_array0[4]; // expected-note{{'global_array0' declared here}} + +void initialized_from_global_array_elt_idx(void) { + int* local = global_array0[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single element from array 'global_array0' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_array_elt_arith_parens(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = (ptrs[0]); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_array_elt_arith_parens_cast(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ((int*) ptrs[0]); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + (++local); +} + +// expected-note@+2{{'ptrs' declared here}} +// expected-note@+1{{'ptrs2' declared here}} +void initialized_from_multiple_array_elt_idx(int* ptrs[__counted_by(size)], int* ptrs2[__counted_by(size)], int size) { + if (size < 2) + return; + // expected-note@+1{{pointer 'local' declared here}} + int* local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = ptrs2[1]; // expected-note{{__single element from array 'ptrs2' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +// expected-note@+2{{'ptrs' declared here}} +// expected-note@+1{{'ptrs2' declared here}} +void initialized_from_multiple_array_elt_arith(int* ptrs[__counted_by(size)], int* ptrs2[__counted_by(size)], int size) { + if (size < 2) + return; + // expected-note@+1{{pointer 'local' declared here}} + int* local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = ptrs2[1]; // expected-note{{__single element from array 'ptrs2' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +//============================================================================== +// Struct member source of __single +//============================================================================== + +struct StructWithSinglePtr0 { + int* ptr; // expected-note{{StructWithSinglePtr0::ptr declared here}} +}; + +struct StructWithSinglePtr1 { + int* ptr; // expected-note{{StructWithSinglePtr1::ptr declared here}} +}; + +struct StructWithSinglePtr2 { + int* ptr; // expected-note{{StructWithSinglePtr2::ptr declared here}} +}; + + +struct StructWithSinglePtr3 { + int* ptr; // expected-note{{StructWithSinglePtr3::ptr declared here}} +}; + +struct StructWithSinglePtr4 { + int* ptr; // expected-note{{StructWithSinglePtr4::ptr declared here}} +}; + +struct StructWithSinglePtr5 { + void* ptr; // expected-note{{StructWithSinglePtr5::ptr declared here}} +}; + +struct StructWithSinglePtr6 { + int* ptr; // expected-note{{StructWithSinglePtr6::ptr declared here}} +}; + +struct StructWithSinglePtr7 { + int* ptr; // expected-note{{StructWithSinglePtr7::ptr declared here}} +}; + +struct StructWithSinglePtr8 { + int* ptr; // expected-note{{StructWithSinglePtr8::ptr declared here}} +}; + +struct StructWithSinglePtr9 { + int* ptr; // expected-note{{StructWithSinglePtr9::ptr declared here}} +}; + +union UnionWithSinglePtr0 { + int* ptr; // expected-note{{UnionWithSinglePtr0::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr1 { + int* ptr; // expected-note{{UnionWithSinglePtr1::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr2 { + int* ptr; // expected-note{{UnionWithSinglePtr2::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr3 { + int* ptr; // expected-note{{UnionWithSinglePtr3::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr4 { + int* ptr; // expected-note{{UnionWithSinglePtr4::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr5 { + void* ptr; // expected-note{{UnionWithSinglePtr5::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr6 { + int* ptr; // expected-note{{UnionWithSinglePtr6::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr7 { + int* ptr; // expected-note{{UnionWithSinglePtr7::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr8 { + int* ptr; // expected-note{{UnionWithSinglePtr8::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr9 { + int* ptr; // expected-note{{UnionWithSinglePtr9::ptr declared here}} + unsigned long long integer; +}; + +void initialized_from_struct_member_idx(struct StructWithSinglePtr0* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_union_member_idx(union UnionWithSinglePtr0* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + + +void initialized_from_struct_member_arith(struct StructWithSinglePtr1* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_union_member_arith(union UnionWithSinglePtr1* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_struct_member_idx(struct StructWithSinglePtr2* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single struct member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_union_member_idx(union UnionWithSinglePtr2* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single union member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_member_arith(struct StructWithSinglePtr3* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single struct member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_union_member_arith(union UnionWithSinglePtr3* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single union member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_struct_member_arith_parens(struct StructWithSinglePtr4* s) { + int* local = (s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_union_member_arith_parens(union UnionWithSinglePtr4* s) { + int* local = (s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_struct_member_arith_parens_cast(struct StructWithSinglePtr5* s) { + int* local = ((int*) s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_union_member_arith_parens_cast(union UnionWithSinglePtr5* s) { + int* local = ((int*) s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_multiple_struct_member_idx(struct StructWithSinglePtr6* s1, struct StructWithSinglePtr7* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = s2->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_union_member_idx(union UnionWithSinglePtr6* u1, union UnionWithSinglePtr7* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = u2->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_struct_member_arith(struct StructWithSinglePtr8* s1, struct StructWithSinglePtr9* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = s2->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_multiple_union_member_arith(union UnionWithSinglePtr8* u1, union UnionWithSinglePtr9* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = u2->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +struct StructWithArrayOfSingles0 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles0::ptrs declared here}} +}; + +struct StructWithArrayOfSingles1 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles1::ptrs declared here}} +}; + +struct StructWithArrayOfSingles2 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles2::ptrs declared here}} +}; + +struct StructWithArrayOfSingles3 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles3::ptrs declared here}} +}; + +struct StructWithArrayOfSingles4 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles4::ptrs declared here}} +}; + +union UnionWithArrayOfSingles0 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles0::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles1 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles1::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles2 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles2::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles3 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles3::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles4 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles4::ptrs declared here}} + int unused; +}; + +void initialized_from_struct_member_via_array_idx(struct StructWithArrayOfSingles0* s) { + int* local = s->ptrs[1]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_member_via_array_idx(struct StructWithArrayOfSingles1* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptrs[1]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_union_member_via_array_idx(union UnionWithArrayOfSingles0* s) { + int* local = s->ptrs[1]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_union_via_array_idx(union UnionWithArrayOfSingles1* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptrs[1]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +struct StructWithArrayOfSingleStructPtrs0 { + struct StructWithArrayOfSingles2* struct_ptrs[4]; +}; + +void initialized_from_struct_member_via_array_nested_idx(struct StructWithArrayOfSingleStructPtrs0* s) { + int* local = s->struct_ptrs[1]->ptrs[2]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +union UnionWithArrayOfSingleUnionPtrs0 { + union UnionWithArrayOfSingles2* union_ptrs[4]; +}; + +void initialized_from_union_member_via_array_nested_idx(union UnionWithArrayOfSingleUnionPtrs0* s) { + int* local = s->union_ptrs[1]->ptrs[2]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_struct_members_via_array_idx(struct StructWithArrayOfSingles3* s1, struct StructWithArrayOfSingles4* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptrs[1]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + local = s2->ptrs[2]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_union_members_via_array_idx(union UnionWithArrayOfSingles3* u1, union UnionWithArrayOfSingles4* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptrs[1]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + local = u2->ptrs[2]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Multiple __single entity types +//============================================================================== +int* global_single_int; // expected-note{{pointer 'global_single_int' declared here}} +int* single_int_source(void); // expected-note{{'single_int_source' declared here}} + +struct StructWithSingleIntPtr { + int* ptr; // expected-note{{StructWithSingleIntPtr::ptr declared here}} +}; + +struct StructWithSingleIntArray { + int* ptrs[4]; // expected-note{{StructWithSingleIntArray::ptrs }} +}; + + +union UnionWithSingleIntPtr { + int* ptr; // expected-note{{UnionWithSingleIntPtr::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSingleIntArray { + int* ptrs[4]; // expected-note{{UnionWithSingleIntArray::ptrs declared here}} + unsigned long long integer; +}; + +void multiple_entity_types( + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{'ptrs' declared here}} + int* ptrs[__counted_by(size)], int size, + struct StructWithSingleIntPtr* s, + union UnionWithSingleIntPtr* u, + struct StructWithSingleIntArray* sa, + union UnionWithSingleIntArray* ua) { + int* local; // expected-note{{pointer 'local' declared here}} + int* __single local_single; // expected-note{{pointer 'local_single' declared here}} + + // Assign multiple entities that are all __single + local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = global_single_int; // expected-note{{__single global 'global_single_int' assigned to 'local' here}} + local = local_single; // expected-note{{__single local variable 'local_single' assigned to 'local' here}} + local = single_int_source(); // expected-note{{__single return value from call to 'single_int_source' assigned to 'local' here}} + local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = s->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = u->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = sa->ptrs[1]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + local = ua->ptrs[1]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// typedefs with VLAs +//============================================================================== + +// FIXME(dliew): Clang currently warns twice because the size expression of the +// VLA type gets visited twice. rdar://117235333 +void typedefs_with_vla(int *x, int size) { // expected-note 2{{pointer 'x' declared here}} + int* local = x; // expected-note 2{{pointer 'local' initialized here}} + for (int i = 0; i < size; ++i) { + // expected-warning@+1 2{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'x' results in a trap if the index expression is >= 1 or < 0}} + typedef int arr[local[i]]; + arr a; // has x[i] elements on each loop iteration + if (i > 0) + a[0] = 0; + } +} + +//============================================================================== +// False positives +//============================================================================== + +void false_positive_reachability_ignored(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; // expected-note{{pointer 'local' initialized here}} + + if (0) { + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; // Not reachable + } +} + +void false_positive_reachability_ignored2(int *p) { // expected-note{{pointer 'p' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + + if (0) { + // expected-note@+1{{pointer 'local' assigned here}} + local = p; // Not reachable + } + + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; +} + +void false_positive_happens_before_ignored(int* p) { // expected-note{{pointer 'p' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; + + // The first __single assignment is made **after** the bad operation. + // To fix this we need the analysis to understand happens-before (rdar://117166345). + local = p; // expected-note{{pointer 'local' assigned here}} +} + +void false_positive_assignment_notes( + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{pointer 'q' declared here}} + int* q, + // expected-note@+1{{pointer 'r' declared here}} + int* r) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + + // This warning is not a false positive because `local` definitely has the + // bounds of a `__single` by the time the unsafe operation happens. + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; + + // These notes are false positives because these assignments happen **after** the unsafe. + // operations. To fix this we need the analysis to understand happens-before (rdar://117166345). + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + local = r; // expected-note{{__single parameter 'r' assigned to 'local' here}} +} + +//============================================================================== +// False negatives +//============================================================================== +void false_neg_multiple_assignment_different_expr(int* p, int* __bidi_indexable q) { + // Multiple assignments from a different expr prevent a diagnostic from being + // emitted. + int* local = p; + local = q; + ++local; // Missing warning +} + +void false_neg_happens_before_ignored(int* p, int* __bidi_indexable q) { + // We fail to warn here because the analysis sees multiple definitions of + // `local` but fails to take into account at `++local` only one definition + // of local is possible. (rdar://117166345) + int* local = p; + ++local; // Missing warning + local = q; +} + +void false_neg_reachability_ignored(int* p, int* __bidi_indexable q) { + // We fail to warn here because the analysis sees multiple definitions of + // `local` but fails to take into account at `++local` only one definition + // of local is possible. + int* local = p; + + if (0) { + local = q; // Not reachable + } + + ++local; // Missing warning +} + +void false_neg_transitive_assignments_ignored(int *p) { + // We fail to warn here because the analysis doesn't track a __single + // indirectly flowing into a local __bidi_indexable via another + // local variable. + int *local = p; + int *local2 = local; + ++local2; // Missing warning +} diff --git a/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c b/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c new file mode 100644 index 0000000000000..955e0d498c7ce --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c @@ -0,0 +1,273 @@ +#include +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +void concrete_to_incomplete(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:13}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_apply_fixit(int *p) { + void *l = (int *__bidi_indexable)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void concrete_to_incomplete_indi(int *p) { + // expected-note@+4{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'int *__single' to 'void *__indexable' creates a '__indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+1{{initializing type 'void *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + void *__indexable l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:25-[[@LINE-1]]:25}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_indi_apply_fixit(int *p) { + void *__indexable l = (int *__bidi_indexable)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void concrete_to_incomplete_bidi(int *p) { + // expected-note@+4{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+1{{initializing type 'void *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + void *__bidi_indexable l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:30-[[@LINE-1]]:30}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_bidi_apply_fixit(int *p) { + void *__bidi_indexable l = (int *__bidi_indexable)p; +} + +void concrete_to_incomplete_explicit_bidi(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:37}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_explicit_bidi_apply_fixit(int *p) { + void *l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_explicit_indi(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__indexable' creates a '__indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = (void *__indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:32-[[@LINE-1]]:32}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_explicit_indi_apply_fixit(int *p) { + void *l = (void *__indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_indi_explicit(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *__indexable l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:49-[[@LINE-1]]:49}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_indi_explicit_apply_fixit(int *p) { + void *__indexable l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_bidi_explicit(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *__bidi_indexable l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:54-[[@LINE-1]]:54}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_bidi_explicit_apply_fixit(int *p) { + void *__bidi_indexable l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void wider_to_narrower(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + char *l = (char *__bidi_indexable)p + 1; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:37}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:37-[[@LINE-2]]:37}:"(char *__single)" +} + +void wider_to_narrower_apply_fixit1(int *p) { + char *l = (char *__bidi_indexable)(int *__bidi_indexable)p + 1; +} + +void wider_to_narrower_apply_fixit2(int *p) { + char *l = (char *__bidi_indexable)(char *__single)p + 1; +} + +// expected-note@+1{{passing argument to parameter here}} +void bidi_arg(char *__bidi_indexable); + +void wider_to_narrower_arg(int *p) { + // expected-warning@+3{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + bidi_arg((char *__bidi_indexable)p); + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:36-[[@LINE-1]]:36}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:36-[[@LINE-2]]:36}:"(char *__single)" +} + +void wider_to_narrower_arg_apply_fixit1(int *p) { + bidi_arg((char *__bidi_indexable)(int *__bidi_indexable)p); +} + +void wider_to_narrower_arg_apply_fixit2(int *p) { + bidi_arg((char *__bidi_indexable)(char *__single)p); +} + +void wider_to_narrower_arg2(int *p) { + // expected-warning@+1{{passing type 'char *__single' to parameter of type 'char *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidi_arg((char *)p); +} + +char *__bidi_indexable wider_to_narrower_ret(int *p) { + // expected-warning@+3{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + return (char *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:34-[[@LINE-1]]:34}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:34}:"(char *__single)" +} + +char *__bidi_indexable wider_to_narrower_ret_apply_fixit1(int *p) { + return (char *__bidi_indexable)(int *__bidi_indexable)p; +} + +char *__bidi_indexable wider_to_narrower_ret_apply_fixit2(int *p) { + return (char *__bidi_indexable)(char *__single)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void *__bidi_indexable concrete_to_incomplete_ret(int *__single p) { + // expected-warning@+4{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+3{{returning type 'int *__single' from a function with result type 'void *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + return p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:10}:"(int *__bidi_indexable)" +} + +void *__bidi_indexable concrete_to_incomplete_ret_apply_fixit(int *__single p) { + return (int *__bidi_indexable)p; +} + +void narrower_to_wider(char *p) { + int *l = (int *__bidi_indexable)p + 1; +} + +void incomplete_to_concrete(void *p) { + char *l = (char *)p; +} + +void incomplete_to_concrete_bidi(void *p) { + // expected-error@+1{{cannot cast from __single pointer to incomplete type 'void *__single' to indexable pointer type 'void *__bidi_indexable'}} + char *l = (void *__bidi_indexable)p; +} + +struct s; + +void opaque_to_concrete(struct s *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'char *__bidi_indexable' from __single pointer to incomplete type 'struct s *__single'; consider declaring pointer 'l' as '__single'}} + char *l = p; // expected-note{{pointer 'l' declared here}} +} + +// This is also undesirable because it's not obvious whether 'struct s' incomplete for the programmer. +void opaque_to_concrete_explicit(struct s *p) { + char *l = (char *)p; +} + +void opaque_to_concrete_explicit2(struct s *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'char *__bidi_indexable' from __single pointer to incomplete type 'struct s *__single'; consider declaring pointer 'l' as '__single'}} + char *l = (struct s *)p; // expected-note{{pointer 'l' declared here}} +} + +void concrete_to_opaque(char *p) { + // expected-note@+4{{cast to 'char *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'char *__single' to 'struct s *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'struct s' having unknown size)}} + // expected-warning@+1{{incompatible pointer types initializing 'struct s *__bidi_indexable' with an expression of type 'char *__single'}} + struct s *l = p; +} + +struct t; + +void opaque_to_opaque(struct t *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'struct s *__bidi_indexable' from __single pointer to incomplete type 'struct t *__single'; consider declaring pointer 'l' as '__single'}} + struct s *l = p; // expected-note{{pointer 'l' declared here}} +} + + +struct flex { + unsigned count; + int arr[__counted_by(count)]; +}; + +void flexible_to_concrete_narrower(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'char *__bidi_indexable' with an expression of type 'struct flex *__single'}} + char *l = p; +} + +void flexible_to_concrete_narrower_apply_fixit1(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'char *__bidi_indexable' with an expression of type 'struct flex *__bidi_indexable'}} + char *l = (struct flex *__bidi_indexable)p; + } + +void flexible_to_concrete_narrower_apply_fixit2(struct flex *p) { + char *l = (char *__single)p; +} + +void flexible_to_concrete_wider(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'long long *__bidi_indexable' with an expression of type 'struct flex *__single'}} + long long *l = p; +} + +void flexible_to_concrete_match(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'unsigned int *__bidi_indexable' with an expression of type 'struct flex *__single'}} + unsigned *l = p; +} + +void flexible_to_incomplete(struct flex *p) { + void *c = p; +} + +void incomplete_to_flexible(void *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'struct flex *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'l' as '__single'}} + struct flex *l = p; // expected-note{{pointer 'l' declared here}} +} + +void concrete_match_to_flexible(unsigned *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'unsigned int *__single'}} + struct flex *l = p; +} + +void concrete_narrower_to_flexible(char *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'char *__single'}} + struct flex *l = p; +} + +void concrete_wider_to_flexible(long long *p) { + // expected-note@+4{{cast to 'long long *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'long long *__single' to 'struct flex *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'struct flex'}} + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'long long *__single'}} + struct flex *l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:20-[[@LINE-1]]:20}:"(long long *__bidi_indexable)" +} + +void concrete_wider_to_flexible_apply_fixit(long long *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'long long *__bidi_indexable'}} + struct flex *l = (long long *__bidi_indexable)p; +} diff --git a/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c b/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c new file mode 100644 index 0000000000000..11d1938f2a940 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c @@ -0,0 +1,110 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=bounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=bounds-safety %s + +#include + +void foo(int *__counted_by(len) ptr, int len) {} + +// bounds-safety-note@+3{{consider adding '__counted_by(len)' to 'ptr'}} +// bounds-safety-note@+2{{consider adding '__counted_by(2)' to 'ptr'}} +// bounds-safety-note@+1{{consider adding '__counted_by(3)' to 'ptr'}} +void bar(int *ptr, int len) { + int *__indexable ptr_bound; + // bounds-safety-note@+2{{count passed here}} + // bounds-safety-warning@+1{{count value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') is invalid for any count other than 0 or 1}} + foo(ptr, len); + foo(ptr, 1); + const int one = 1; + foo(ptr, one); + const int zero = 0; + foo(ptr, zero); + const int two = 2; + // bounds-safety-error@+1{{passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 2 always fails}} + foo(ptr, two); + const int neg = -1; + // bounds-safety-error@+1{{negative count value of -1 for 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(ptr, neg); + + foo(ptr_bound, len); + foo(ptr_bound, 1); + foo(ptr_bound, zero); + foo(ptr_bound, two); + // bounds-safety-error@+1{{negative count value of -1 for 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(ptr_bound, neg); + + int local_len; + // bounds-safety-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __counted_by(local_len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + int *__counted_by(local_len) local_ptr = ptr; + int *__counted_by(1) local_ptr2 = ptr; + // bounds-safety-error@+1{{initializing 'local_ptr3' of type 'int *__single __counted_by(2)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + int *__counted_by(2) local_ptr3 = ptr; + const int three = 3; + // bounds-safety-error@+1{{initializing 'local_ptr4' of type 'int *__single __counted_by(3)' (aka 'int *__single') and count value of 3 with 'int *__single' always fails}} + int *__counted_by(three) local_ptr4 = ptr; + const int neg2 = -1; + // bounds-safety-error@+1{{negative count value of -1 for 'local_ptr5' of type 'int *__single __counted_by(-1)' (aka 'int *__single')}} + int *__counted_by(neg2) local_ptr5 = ptr; +} + +void baz(int *__counted_by(len) ptr1, int *__counted_by(1) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // bounds-safety-warning@+1{{count value is not statically known: assigning to 'ptr1' of type 'int *__single __counted_by(len)' (aka 'int *__single') from 'int *__single' is invalid for any count other than 0 or 1}} + ptr1 = sp; + // bounds-safety-note@+1{{count assigned here}} + len = len; + + ptr2 = sp; + ptr2 = up; // bounds-safety-error{{assigning to 'int *__single __counted_by(1)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void foo_nullable(int *__counted_by_or_null(len) ptr, int len) {} + +// bounds-safety-note@+1{{consider adding '__counted_by_or_null(len)' to 'ptr'}} +void bar_nullable(int *ptr, int len) { + int *__indexable ptr_bound; + // bounds-safety-note@+2{{count passed here}} + // bounds-safety-warning@+1{{count value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') is invalid for any count other than 0 or 1 unless the pointer is null}} + foo_nullable(ptr, len); + foo_nullable(ptr, 1); + const int one = 1; + foo_nullable(ptr, one); + const int zero = 0; + foo_nullable(ptr, zero); + const int two = 2; + foo_nullable(ptr, two); + const int neg = -1; + // bounds-safety-error@+1{{possibly passing non-null to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + foo_nullable(ptr, neg); + + foo_nullable(ptr_bound, len); + foo_nullable(ptr_bound, 1); + foo_nullable(ptr_bound, zero); + foo_nullable(ptr_bound, two); + // bounds-safety-error@+1{{possibly passing non-null to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + foo_nullable(ptr_bound, neg); + + int local_len; + // bounds-safety-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __counted_by_or_null(local_len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + int *__counted_by_or_null(local_len) local_ptr = ptr; + int *__counted_by_or_null(1) local_ptr2 = ptr; + int *__counted_by_or_null(2) local_ptr3 = ptr; + const int three = 3; + int *__counted_by_or_null(three) local_ptr4 = ptr; + const int neg2 = -1; + // bounds-safety-error@+1{{possibly initializing 'local_ptr5' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int *__counted_by_or_null(neg2) local_ptr5 = ptr; +} + +void baz_nullable(int *__counted_by_or_null(len) ptr1, int *__counted_by_or_null(1) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // bounds-safety-warning@+1{{count value is not statically known: assigning to 'ptr1' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') from 'int *__single' is invalid for any count other than 0 or 1 unless the pointer is null}} + ptr1 = sp; + // bounds-safety-note@+1{{count assigned here}} + len = len; + + ptr2 = sp; + ptr2 = up; // bounds-safety-error{{assigning to 'int *__single __counted_by_or_null(1)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} diff --git a/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c b/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c new file mode 100644 index 0000000000000..059887fb26af4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void foo(int *__sized_by(len) ptr, int len) {} +void foo_void(void *__sized_by(len) ptr, int len) {} +void foo_fixed(void *__sized_by(8) ptr) {} + +// expected-note@+4{{consider adding '__sized_by(len)' to 'ptr'}} +// expected-note@+3{{consider adding '__sized_by(8)' to 'ptr'}} +// expected-note@+2{{consider adding '__sized_by(5)' to 'ptr'}} +// expected-note@+1{{consider adding '__sized_by(12)' to 'ptr'}} +void bar(int *ptr, int len) { + int *ptr_auto_bound; + // expected-note@+2{{size passed here}} + // expected-warning@+1{{size value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single') is invalid for any size greater than 4}} + foo(ptr, len); + foo(ptr, 1); + foo(ptr, sizeof(int)); + const int four = 1 * sizeof(int); + foo(ptr, four); + const int zero = 0; + foo(ptr, zero); + const int eight = 2 * sizeof(int); + foo(ptr, eight); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single') with size value of 8 always fails}} + const int neg = -1; + foo(ptr, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single')}} + foo_fixed(ptr); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'void *__single __sized_by(8)' (aka 'void *__single') with size value of 8 always fails}} + + foo_void(ptr, zero); + foo_void(ptr, four); + foo_void(ptr, eight); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') with size value of 8 always fails}} + foo_void(ptr, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single')}} + + foo(ptr_auto_bound, len); + foo(ptr_auto_bound, 1); + foo(ptr_auto_bound, zero); + foo(ptr_auto_bound, eight); + foo(ptr_auto_bound, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single')}} + + int local_len; + // expected-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __sized_by(local_len)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + int *__sized_by(local_len) local_ptr = ptr; + int *__sized_by(4) local_ptr2 = ptr; + // expected-error@+1{{initializing 'local_ptr3' of type 'int *__single __sized_by(5)' (aka 'int *__single') and size value of 5 with 'int *__single' and pointee of size 4 always fails}} + int *__sized_by(5) local_ptr3 = ptr; + const int three = 3 * sizeof(int); + // expected-error@+1{{initializing 'local_ptr4' of type 'int *__single __sized_by(12)' (aka 'int *__single') and size value of 12 with 'int *__single' and pointee of size 4 always fails}} + int *__sized_by(three) local_ptr4 = ptr; + const int neg2 = -1; + // expected-error@+1{{negative size value of -1 for 'local_ptr5' of type 'int *__single __sized_by(-1)' (aka 'int *__single')}} + int *__sized_by(neg2) local_ptr5 = ptr; +} + +void baz(int *__sized_by(len) ptr1, int *__sized_by(4) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // expected-warning@+1{{size value is not statically known: assigning to 'ptr1' of type 'int *__single __sized_by(len)' (aka 'int *__single') from 'int *__single' is invalid for any size greater than 4}} + ptr1 = sp; + // expected-note@+1{{size assigned here}} + len = len; + ptr1 = up; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + ptr2 = sp; + ptr2 = up; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +struct qux; + +void quux(void *__sized_by(42) ptr1, + void *__sized_by(0) ptr2, + void *__sized_by(len) ptr3, int len, + struct qux *__single qux) { + // expected-error@+1{{assigning to 'ptr1' of type 'void *__single __sized_by(42)' (aka 'void *__single') with size value of 42 from 'struct qux *__single' with pointee of size 0 always fails}} + ptr1 = qux; + + ptr2 = qux; // ok + + // expected-error@+1{{assigning to 'ptr3' of type 'void *__single __sized_by(len)' (aka 'void *__single') with size value of 42 from 'struct qux *__single' with pointee of size 0 always fails}} + ptr3 = qux; + len = 42; +} + +struct unsized; + +void external(struct unsized *__sized_by(size) p, int size); + +void convert_unsized(struct unsized *__single p, int size) { + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + struct unsized *__single __sized_by(size) a = p; // requires explicit __single because of rdar://112196572 + // expected-warning@+1{{size value is not statically known: passing 'struct unsized *__single' to parameter 'p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') is invalid for any size greater than 0}} + external(p, size); // expected-note{{size passed here}} +} + +void convert_unsized2(struct unsized *__single p, int size) { + // expected-note@+1{{size initialized here}} + int s = size; + // expected-warning@+1{{size value is not statically known: initializing 'a' of type 'struct unsized *__single __sized_by(s)' (aka 'struct unsized *__single') with 'struct unsized *__single' is invalid for any size greater than 0}} + struct unsized *__single __sized_by(s) a = p; // requires explicit __single because of rdar://112196572 + // expected-warning@+1{{size value is not statically known: passing 'struct unsized *__single' to parameter 'p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') is invalid for any size greater than 0}} + external(p, size); // expected-note{{size passed here}} +} diff --git a/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c b/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c new file mode 100644 index 0000000000000..db66f002f57ce --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +extern struct a __a; + +struct b { + void *a; +}; + +struct b b = { + .a = &__a, +}; + +void foo(struct b b, struct a * a) { + a = b.a; +} + +struct c; + +void bar(struct c * c, struct a * a) { + a = c; // expected-warning{{incompatible pointer types assigning to 'struct a *__single' from 'struct c *__single'}} +} diff --git a/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c b/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c new file mode 100644 index 0000000000000..277878d0c6e45 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c @@ -0,0 +1,767 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -verify=expected,legacy,extra %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -verify=expected,legacy,cli %s +#include + +void consume_sb(int* __sized_by(3) p); +void consume_sbon(int* __sized_by_or_null(3) p); + +struct sb { + int count; + int* __sized_by(count) buf; +}; + +struct sbon { + int count; + int* __sized_by_or_null(count) buf; +}; + +void side_effect(void); + +int global_arr [2] = {0}; +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(2) global_sb = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(2) global_sbon = global_arr; + +const int const_size = 2; +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(const_size) global_sb_const_qual_count = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(const_size) global_sbon_const_qual_count = global_arr; + +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(1+1) global_sb_opo = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(1+1) global_sbon_opo = global_arr; + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(2) test_sb(int* __sized_by(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(2) local_sb = p; + local_sb = p; // OK + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + --global_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb = global_sb + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(2) test_sb_constant_fold_count(int* __sized_by(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(1+1) local_sb = p; + local_sb = p; // OK + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sb_opo++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb_opo; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_opo--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + --global_sb_opo; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_opo += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_opo -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb_opo++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb_opo = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb_opo-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb_opo = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_opo = global_sb_opo + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(size) test_sb_const_qualified_size(const int size, int* __sized_by(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(local_size) local_sb = p; + side_effect(); + local_sb = p; // OK + + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_sb_const_qual_count++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb_const_qual_count; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --global_sb_const_qual_count; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_const_qual_count += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_const_qual_count = global_sb_const_qual_count + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(2) test_sbon(int* __sized_by_or_null(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(2) local_sbon = p; + local_sbon = p; // OK + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + --global_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon = global_sbon + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(2) test_sbon_constant_fold_count(int* __sized_by_or_null(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(1+1) local_sbon = p; + local_sbon = p; // OK + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sbon_opo++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon_opo; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_opo--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + --global_sbon_opo; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_opo += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_opo -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon_opo++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon_opo = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon_opo-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon_opo = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_opo = global_sbon_opo + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(size) test_sbon_const_qualified_size(const int size, int* __sized_by_or_null(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(local_size) local_sbon = p; + side_effect(); + local_sbon = p; // OK + + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_sbon_const_qual_count++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon_const_qual_count; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --global_sbon_const_qual_count; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_const_qual_count += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_const_qual_count = global_sbon_const_qual_count + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} +} + +// Warning diagnostic tests + +void downgrade_to_warning(int* __sized_by(4) ptr) { // expected-note{{__sized_by attribute is here}} +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // expected-warning{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 4 always traps}} +#pragma clang diagnostic pop +} + +void downgrade_to_ignored(int* __sized_by(4) ptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // ok +} diff --git a/clang/test/BoundsSafety/Sema/sized_by_incdec.c b/clang/test/BoundsSafety/Sema/sized_by_incdec.c new file mode 100644 index 0000000000000..157729a9b9a32 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/sized_by_incdec.c @@ -0,0 +1,159 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__sized_by(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __sized_by(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void FooOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __sized_by_or_null(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void Bar(int *__sized_by(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void BarOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void TestPtrPostIncrement(int *__sized_by(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestPtrPostIncrementOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestLenPostIncrementOrNull(int *__sized_by_or_null(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestLenPostIncrement(int *__sized_by(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestPtrPreDecrement(int *__sized_by(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} +} + +void TestPtrPreDecrementOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} +} + +typedef struct { + char *__sized_by(len1 + len2) buf; + unsigned len1; + unsigned len2; +} S; + +void TestMultipleCounts1(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__sized_by_or_null(len1 + len2) buf; + unsigned len1; + unsigned len2; +} SOrNull; + +void TestMultipleCounts1OrNull(SOrNull *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2OrNull(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3OrNull(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__sized_by(len) buf; + unsigned len; +} T; + +void Baz(T *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void Qux(T *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void Quux(T *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void Quuz(T *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void Corge(T *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +typedef struct { + char *__sized_by_or_null(len) buf; + unsigned len; +} TOrNull; + +void BazOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void QuxOrNull(TOrNull *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void QuuxOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void QuuzOrNull(TOrNull *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void CorgeOrNull(TOrNull *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} diff --git a/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c b/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c new file mode 100644 index 0000000000000..b81f5858fafe5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c @@ -0,0 +1,96 @@ +// RUN: not %clang_cc1 -fbounds-safety -fsyntax-only %s 2>&1 | FileCheck %s +#include + +// This test is essentially a test of +// `SourceLocationFor(const CountAttributedType *CATy, Sema &S)`. The test is +// checking the column number attached the diagnostic. +// FIXME: This should probably be a unit test. We can't test `__sized_by` or +// `__sized_by_or_null` here because the diagnostic we are relying isn't emitted +// for those attributes. + +struct IncompleteTy; // Incomplete + +//============================================================================== +// counted_by +//============================================================================== + +#define custom_counted_by(X) __attribute__((counted_by(X))) + +#define custom_counted_by__(X) __attribute__((__counted_by__(X))) + +#define COUNT_ARG 0 +struct CB { + int count; + // CHECK: [[@LINE+1]]:26: note: __counted_by attribute is here + struct IncompleteTy* __counted_by(count) b_macro; + // CHECK: [[@LINE+1]]:41: note: __counted_by attribute is here + struct IncompleteTy* __attribute__((counted_by(count))) b_direct_non_affixed; + // CHECK: [[@LINE+1]]:41: note: __counted_by attribute is here + struct IncompleteTy* __attribute__((__counted_by__(count))) b_direct_affixed; + // NOTE: In these cases the locations just point to the count expressions as + // a fallback. + // CHECK: [[@LINE+1]]:44: note: __counted_by attribute is here + struct IncompleteTy* custom_counted_by(count) b_custom_macro; + // CHECK: [[@LINE+1]]:46: note: __counted_by attribute is here + struct IncompleteTy* custom_counted_by__(count) b_custom_macro_underscored; + + // CHECK: [[@LINE+1]]:39: note: __counted_by attribute is here + struct IncompleteTy* __counted_by(COUNT_ARG) b_macro_macro_arg; + // CHECK: [[@LINE+1]]:52: note: __counted_by attribute is here + struct IncompleteTy* __attribute__((counted_by(COUNT_ARG))) b_direct_non_affixed_macro_arg; + // CHECK: [[@LINE+1]]:56: note: __counted_by attribute is here + struct IncompleteTy* __attribute__((__counted_by__(COUNT_ARG))) b_direct_affixed_macro_arg; +}; + +void useCB(struct CB* cb) { + struct IncompleteTy* local0 = cb->b_macro; + struct IncompleteTy* local1 = cb->b_direct_non_affixed; + struct IncompleteTy* local2 = cb->b_direct_affixed; + struct IncompleteTy* local3 = cb->b_custom_macro; + struct IncompleteTy* local4 = cb->b_custom_macro_underscored; + struct IncompleteTy* local5 = cb->b_macro_macro_arg; + struct IncompleteTy* local6 = cb->b_direct_non_affixed_macro_arg; + struct IncompleteTy* local7 = cb->b_direct_affixed_macro_arg; +} + +//============================================================================== +// counted_by_or_null +//============================================================================== + +#define custom_counted_by_or_null(X) __attribute__((counted_by_or_null(X))) +#define custom_counted_by_or_null__(X) __attribute__((__counted_by_or_null__(X))) + +struct CBON { + int count; + // CHECK: [[@LINE+1]]:26: note: __counted_by_or_null attribute is here + struct IncompleteTy* __counted_by_or_null(count) b_macro; + // CHECK: [[@LINE+1]]:41: note: __counted_by_or_null attribute is here + struct IncompleteTy* __attribute__((counted_by_or_null(count))) b_direct_non_affixed; + // CHECK: [[@LINE+1]]:41: note: __counted_by_or_null attribute is here + struct IncompleteTy* __attribute__((__counted_by_or_null__(count))) b_direct_affixed; + // NOTE: In these cases the locations just points to the count expressions as + // a fallback. + // CHECK: [[@LINE+1]]:52: note: __counted_by_or_null attribute is here + struct IncompleteTy* custom_counted_by_or_null(count) b_custom_macro; + // CHECK: [[@LINE+1]]:54: note: __counted_by_or_null attribute is here + struct IncompleteTy* custom_counted_by_or_null__(count) b_custom_macro_underscored; + + // CHECK: [[@LINE+1]]:47: note: __counted_by_or_null attribute is here + struct IncompleteTy* __counted_by_or_null(COUNT_ARG) b_macro_macro_arg; + // CHECK: [[@LINE+1]]:60: note: __counted_by_or_null attribute is here + struct IncompleteTy* __attribute__((counted_by_or_null(COUNT_ARG))) b_direct_non_affixed_macro_arg; + // CHECK: [[@LINE+1]]:64: note: __counted_by_or_null attribute is here + struct IncompleteTy* __attribute__((__counted_by_or_null__(COUNT_ARG))) b_direct_affixed_macro_arg; +}; + + +void useCBON(struct CBON* cbon) { + struct IncompleteTy* local0 = cbon->b_macro; + struct IncompleteTy* local1 = cbon->b_direct_non_affixed; + struct IncompleteTy* local2 = cbon->b_direct_affixed; + struct IncompleteTy* local3 = cbon->b_custom_macro; + struct IncompleteTy* local4 = cbon->b_custom_macro_underscored; + struct IncompleteTy* local5 = cbon->b_macro_macro_arg; + struct IncompleteTy* local6 = cbon->b_direct_non_affixed_macro_arg; + struct IncompleteTy* local7 = cbon->b_direct_affixed_macro_arg; +} diff --git a/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c b/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c new file mode 100644 index 0000000000000..f3eeb89a45e03 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c @@ -0,0 +1,103 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +struct T { + void (*fp)(const struct T *t); + int i; +}; + +static void foo(const struct T *t) {} + +const struct T t = { + .fp = foo, + .i = 0, +}; + +// BoundsSafetyPointerCast Evaluation Results +// Types - B: __bidi_indexable, T: __single, U: __unsafe_indexable, A: __indexable +// S: CString, C: Count +// Result - P: Compile-time constant, F: Compile-time error, +// R: No compile-time constant, Z: P and Zero-bound, +// D: Depending on the metadata +// * (at least one elem) +// ^ (upper bounds check) +// < (if the destination type size is smaller) + +// To | B | T | U | A | S | C | +// From| | | | | | | +// ---------------------------- +// B | P | R | P | R | R | R | +// T | P | P<| P | P | Z | RD| +// U | F | P | P | F | F | F | +// A | P | R | P | P | R | R | +// S | P | P | P | P*| P | R | +// C | P | D | P | P | R | R | + +int g_var = 0; + +// B -> B: P +int *__bidi_indexable bi_p = &g_var; +// B -> A: R^ +int *__indexable fi_p = &g_var; +// B -> T: R +int *__single s_p = &g_var; +// B -> U: P +int *__unsafe_indexable ui_p = &g_var; + +// T -> B: P +// expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type}} +void *__bidi_indexable bi_p3 = foo; // expected-note{{pointer 'bi_p3' declared here}} +// T -> A: P +// expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type}} +void *__indexable fi_p3 = foo; // expected-note{{pointer 'fi_p3' declared here}} +// T -> T: P +void *__single s_p3 = foo; +// T -> U: P +void *__unsafe_indexable ui_p2 = foo; + +void Test () { + static int len1; + // B -> C: R + // expected-warning@+1{{possibly initializing 'c_p1' of type 'int *__single __counted_by(len1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + static int *__counted_by(len1) c_p1 = &g_var; // expected-error{{initializer element is not a compile-time constant}} + static int len2; + // T -> C: RD + +#pragma mark - UNEXPECTEDLY PASSES + // expected-warning@+1{{possibly initializing 'c_p2' of type 'void *__single __sized_by(len2)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + static void *__sized_by(len2) c_p2 = foo; // expected-error{{initializer element is not a compile-time constant}} + static int len3 = 1; + // T -> C: RD + // expected-error@+1{{initializing 'c_p3' of type 'void *__single __sized_by(len3)' (aka 'void *__single') and size value of 1 with 'void (*__single)(const struct T *__single)' and pointee of size 0 always fails}} + static void *__sized_by(len3) c_p3 = foo; // expected-error{{initializer element is not a compile-time constant}} +#pragma mark - + + static int s_var = 0; + static int *__counted_by(10) c_p4 = &s_var; // expected-error{{initializer element is not a compile-time constant}} + + static int s_arr[10]; + static int *__counted_by(10) c_p5 = s_arr; // ok + + static const int len4 = 1; + static int *__counted_by(len4) c_p6 = &g_var; // ok +} + +struct RangedStruct { + int *__ended_by(end) start; + int *end; +}; + +// TODO +void TestEndedBy() { + // expected-error@+2{{initializer element is not a compile-time constant}} + static struct RangedStruct rs = { + 0, 0 + }; + + static char s_arr[10]; + // expected-error@+1{{initializer element is not a compile-time constant}} + static char *end = &s_arr[10]; + // expected-error@+1{{initializer element is not a compile-time constant}} + static char *__ended_by(end) start = &s_arr[0]; +} diff --git a/clang/test/BoundsSafety/Sema/system-header-func-decl.h b/clang/test/BoundsSafety/Sema/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c new file mode 100644 index 0000000000000..4c4ac33d91464 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include +#include "system-header-func-decl.h" + +int *__single foo(int *__single *__single); + +void bar(void) { + int *__single s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c new file mode 100644 index 0000000000000..1329f4f7bad28 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include +#include "system-header-func-decl.h" + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c new file mode 100644 index 0000000000000..76ba816224972 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include + +int *__single foo(int *__single *__single); + +#include "system-header-func-decl.h" + +void bar(void) { + int *__single s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c new file mode 100644 index 0000000000000..7ca4142f37160 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +#include "system-header-func-decl.h" + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c new file mode 100644 index 0000000000000..b149a8d0d579d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +#include +#include "system-header-func-decl.h" + +// expected-error@+1{{conflicting types for 'foo'}} +int *__single foo(int *__indexable *__single); diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c new file mode 100644 index 0000000000000..49a10abd30ef0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +#include +#include "system-header-func-decl.h" + +// expected-error@+1{{conflicting types for 'foo'}} +int *__single foo(int *__single *__indexable); diff --git a/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp b/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp new file mode 100644 index 0000000000000..6cccff6feebc4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp @@ -0,0 +1,37 @@ + +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -std=c++17 -verify %s + +#include + +// expected-no-diagnostics + +struct true_type { + static constexpr bool value = true; +}; + +struct false_type { + static constexpr bool value = false; +}; + +template +struct is_ptr : false_type {}; + +template +struct is_ptr : true_type {}; + +template +inline constexpr bool is_ptr_v = is_ptr::value; + +int main() { + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c b/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c new file mode 100644 index 0000000000000..ec9781bb5b1c6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Empty { + int a[__null_terminated 0]; // expected-error{{'__terminated_by' attribute cannot be applied to empty arrays}} +}; + +void empty(void) { + int a[__null_terminated 0]; // expected-error{{'__terminated_by' attribute cannot be applied to empty arrays}} + int b[__null_terminated] = {}; // expected-error{{incomplete array 'b' with '__terminated_by' attribute must be initialized with at least one element}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-init.c b/clang/test/BoundsSafety/Sema/terminated-by-array-init.c new file mode 100644 index 0000000000000..c87019d3ca8cd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-init.c @@ -0,0 +1,426 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +typedef __WCHAR_TYPE__ wchar_t; +typedef __CHAR16_TYPE__ char16_t; +typedef __CHAR32_TYPE__ char32_t; + +struct Foo { + int a[__null_terminated 2]; + int b[__terminated_by(42) 2]; +}; + +struct Bar { + int a[__null_terminated 2]; +}; + +struct Baz { + char a[__terminated_by('X') 3]; + char b[__null_terminated 3]; +}; + +struct Qux { + struct Foo foo; + struct Bar bar; + struct Baz baz; +}; + +struct Quux { + const char *__null_terminated p; +}; + +void explicit_const_init(void) { + // ok + int a1[__null_terminated 3] = {1, 2, 0}; + + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + int a2[__null_terminated 3] = {1, 2, 3}; + + // ok + int a3[__terminated_by(42) 3] = {1, 2, 42}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + int a4[__terminated_by(42) 3] = {1, 2, 0}; + + // ok + char s1[__null_terminated 3] = "HI"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + char s2[__terminated_by('X') 3] = "HI"; + + // ok + struct Foo foo1 = {{1, 0}, {2, 42}}; + + // expected-error@+1{{array 'foo2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + struct Foo foo2 = {{1, 1}, {2, 42}}; + + // expected-error@+2{{array 'foo3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 2)}} + struct Foo foo3 = {{1, 1}, {2, 2}}; + + // ok + struct Qux qux1 = {{{1, 0}, {2, 42}}, {{1, 0}}, {{'Z', 'Y', 'X'}, "HI"}}; + + // expected-error@+5{{array 'qux2.foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + // expected-error@+4{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 2)}} + // expected-error@+3{{array 'qux2.bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + // expected-error@+2{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + // expected-error@+1{{array 'qux2.baz.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 'X')}} + struct Qux qux2 = {{{1, 1}, {2, 2}}, {{1, 3}}, {"HI", {'Z', 'Y', 'X'}}}; +} + +void explicit_const_init_excess(void) { + // expected-warning@+1{{excess elements in array initializer}} + int a1[__null_terminated 3] = {1, 2, 0, 4}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + int a2[__null_terminated 3] = {1, 2, 3, 4}; + + // expected-warning@+1{{excess elements in array initializer}} + int a3[__terminated_by(42) 3] = {1, 2, 42, 4}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + int a4[__terminated_by(42) 3] = {1, 2, 0, 42}; + + // ok + char s1[__null_terminated 3] = "HI\0"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 'J')}} + char s2[__terminated_by('X') 3] = "HEJ"; + + // expected-warning@+1{{initializer-string for char array is too long}} + char s3[__null_terminated 3] = "HI\0HI"; + + // expected-warning@+2{{initializer-string for char array is too long}} + // expected-error@+1{{array 's4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 'C')}} + char s4[__terminated_by('X') 3] = "ABCD"; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-warning@+1{{excess elements in array initializer}} + struct Foo foo1 = {{1, 0, 1}, {2, 42, 0}}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'foo2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + struct Foo foo2 = {{1, 1, 0}, {2, 42}}; + + // expected-warning@+4{{excess elements in array initializer}} + // expected-warning@+3{{excess elements in array initializer}} + // expected-error@+2{{array 'foo3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 2)}} + struct Foo foo3 = {{1, 1, 0}, {2, 2, 42}}; + + // ok + struct Baz baz1 = {"ZYX", "HI\0"}; + + // expected-warning@+2{{initializer-string for char array is too long}} + // expected-warning@+1{{initializer-string for char array is too long}} + struct Baz baz2 = {"ZYXW", "HI\0HI"}; + + // expected-warning@+4{{initializer-string for char array is too long}} + // expected-warning@+3{{initializer-string for char array is too long}} + // expected-error@+2{{array 'baz3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 'C')}} + // expected-error@+1{{array 'baz3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 'X')}} + struct Baz baz3 = {"ABCD", "ZYXW"}; + + // expected-warning@+4{{excess elements in array initializer}} + // expected-warning@+3{{excess elements in array initializer}} + // expected-warning@+2{{excess elements in array initializer}} + // expected-warning@+1{{excess elements in array initializer}} + struct Qux qux1 = {{{1, 0, 1}, {2, 42, 0}}, {{1, 0, 1}}, {{'Z', 'Y', 'X', 'W'}, "HI\0"}}; + + // expected-warning@+8{{excess elements in array initializer}} + // expected-warning@+7{{excess elements in array initializer}} + // expected-warning@+6{{excess elements in array initializer}} + // expected-error@+5{{array 'qux2.foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 1)}} + // expected-error@+4{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 2)}} + // expected-error@+3{{array 'qux2.bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + // expected-error@+2{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 'A')}} + // expected-error@+1{{array 'qux2.baz.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 'J')}} + struct Qux qux2 = {{{1, 1, 0}, {2, 2}}, {{1, 3, 0}}, {{'Z', 'Y', 'A', 'W'}, "HEJ"}}; +} + +void implicit_init(void) { + // ok + int a1[__null_terminated 3] = {}; + + // ok + int a2[__null_terminated 3] = {1, 2}; + + // expected-error@+1{{array 'a3' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + int a3[__terminated_by(42) 3] = {}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + int a4[__terminated_by(42) 3] = {1, 2}; + + // ok + char s1[__null_terminated 3] = ""; + + // ok + char s2[__null_terminated 3] = "X"; + + // expected-error@+1{{array 'foo1.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + struct Foo foo1 = {}; + + // expected-error@+1{{array 'foo2.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + struct Foo foo2 = {{}}; + + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + struct Foo foo3 = {{}, {}}; + + // ok + struct Foo foo4 = {{}, {1, 42}}; + + // ok + struct Foo foo5 = {{1}, {1, 42}}; + + // ok + struct Bar bar1 = {}; + + // ok + struct Bar bar2 = {{}}; + + // expected-error@+1{{array 'baz1.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Baz baz1 = {}; + + // expected-error@+1{{array 'baz2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Baz baz2 = {{}}; + + // expected-error@+1{{array 'baz3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Baz baz3 = {{}, {}}; + + // ok + struct Baz baz4 = {"ZYX"}; + + // expected-error@+2{{array 'qux1.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + // expected-error@+1{{array 'qux1.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Qux qux1 = {}; + + // expected-error@+2{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + // expected-error@+1{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Qux qux2 = {{}, {}, {}}; + + // expected-error@+2{{array 'qux3.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + // expected-error@+1{{array 'qux3.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + struct Qux qux3 = {{{}, {}}, {{}}, {{}, {}}}; + + // ok + struct Qux qux4 = {{{}, {1, 42}}, {}, {{'Z', 'Y', 'X'}}}; + + // ok + struct Qux qux5 = {{{}, {1, 42}}, {}, {{'Z', 'Y', 'X'}, {}}}; +} + +void explicit_non_const_init(int i, char c) { + // expected-error@+1{{terminator in array 'a1' must be a compile-time constant}} + int a1[__null_terminated 3] = {1, 2, i}; + + // ok + int a2[__null_terminated 3] = {i, i, 0}; + + // expected-error@+5{{terminator in array 'qux1.foo.a' must be a compile-time constant}} + // expected-error@+4{{terminator in array 'qux1.foo.b' must be a compile-time constant}} + // expected-error@+3{{terminator in array 'qux1.bar.a' must be a compile-time constant}} + // expected-error@+2{{terminator in array 'qux1.baz.a' must be a compile-time constant}} + // expected-error@+1{{terminator in array 'qux1.baz.b' must be a compile-time constant}} + struct Qux qux1 = {{{0, i}, {0, i}}, {{0, i}}, {{'A', 'A', c}, {'A', 'A', c}}}; + + // ok + struct Qux qux2 = {{{i, 0}, {i, 42}}, {{i, 0}}, {{c, c, 'X'}, {c, c, '\0'}}}; +} + +void incomplete_array_init(void) { + // ok + int a1[__null_terminated] = {1, 2, 0}; + + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + int a2[__null_terminated] = {1, 2, 3}; + + // ok + int a3[__null_terminated] = (int[3]){1, 2, 0}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 3)}} + int a4[__null_terminated] = (int[3]){1, 2, 3}; + + // ok + char s1[__null_terminated] = "HI"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0)}} + char s2[__terminated_by('X')] = "HI"; +} + +void string_literal_init(void) { + // ok + char s1[__null_terminated 3] = "HI"; + + // ok + wchar_t s2[__null_terminated 3] = L"HI"; + + // ok + char16_t s3[__null_terminated 3] = u"HI"; + + // ok + char32_t s4[__null_terminated 3] = U"HI"; + + // ok + char s5[__terminated_by('X') 3] = "ZYX"; + + // ok + wchar_t s6[__terminated_by(L'X') 3] = L"ZYX"; + + // ok + char16_t s7[__terminated_by(u'X') 3] = u"ZYX"; + + // ok + char32_t s8[__terminated_by(U'X') 3] = U"ZYX"; + + // expected-error@+1{{array 's9' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 'C')}} + char s9[__terminated_by('X') 3] = "ABC"; + + // expected-error@+1{{array 's10' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: L'X'; got L'C')}} + wchar_t s10[__terminated_by(L'X') 3] = L"ABC"; + + // expected-error@+1{{array 's11' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: u'X'; got u'C')}} + char16_t s11[__terminated_by(u'X') 3] = u"ABC"; + + // expected-error@+1{{array 's12' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: U'X'; got U'C')}} + char32_t s12[__terminated_by(U'X') 3] = U"ABC"; + + // ok + wchar_t s13[__terminated_by(U'\U0010F00C') 3] = L"\U0010F00A\U0010F00B\U0010F00C"; + + // ok + char16_t s14[__terminated_by(u'\u1122') 3] = u"\u1120\u1121\u1122"; + + // ok + char32_t s15[__terminated_by(U'\U0010F00C') 3] = U"\U0010F00A\U0010F00B\U0010F00C"; +} + +void sign_test(void) { + // ok + signed int a1[__terminated_by(-1) 2] = {0, -1}; + + // ok + unsigned int a2[__terminated_by(-1) 2] = {0, -1}; + + // ok + unsigned long long a3[__terminated_by(-1) 2] = {0, -1}; +} + +void array_of_pointers(void) { + // ok + char *__null_terminated a1[__null_terminated 3] = {"foo", "bar", 0}; + + // expected-error@+1{{terminator in array 'a2' must be a compile-time constant}} + char *__null_terminated a2[__null_terminated 3] = {"foo", "bar", "baz"}; + + // ok + char *__null_terminated a3[__null_terminated 3] = {"foo", 0, 0}; + + // ok + char *__null_terminated a4[__null_terminated 3] = {}; + + // ok + char *__null_terminated a5[__null_terminated 3] = {0}; + + // ok + char *__null_terminated a6[__null_terminated 3] = {"foo"}; + + // ok + char *__null_terminated a7[3] = {}; + + // ok + char *__null_terminated a8[3] = {0}; + + // ok + char *__null_terminated a9[3] = {"foo"}; +} + +void foo_as_param(struct Foo foo); +void bar_as_param(struct Bar bar); +void quux_as_param(struct Quux quux); + +void as_param(void) { + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + foo_as_param((struct Foo){}); + + // expected-error@+2{{array 'foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 2)}} + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 4)}} + foo_as_param((struct Foo){{1, 2}, {3, 4}}); + + // ok + foo_as_param((struct Foo){{1, 0}, {3, 42}}); + + // ok + bar_as_param((struct Bar){}); + + // expected-error@+1{{array 'bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 2)}} + bar_as_param((struct Bar){{1, 2}}); + + // ok + bar_as_param((struct Bar){{1, 0}}); +} + +struct Foo foo_as_ret(void) { + // expected-error@+1{{array '.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + return (struct Foo){}; + + // expected-error@+2{{array '.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 2)}} + // expected-error@+1{{array '.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 4)}} + return (struct Foo){{1, 2}, {3, 4}}; + + // ok + return (struct Foo){{1, 0}, {3, 42}}; +} + +struct Bar bar_as_ret(void) { + // ok + return (struct Bar){}; + + // expected-error@+1{{array '.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 0; got 2)}} + return (struct Bar){{1, 2}}; + + // ok + return (struct Bar){{1, 0}}; +} + +struct Foo copy_init(void) { + struct Foo foo1 = {{1, 0}, {2, 42}}; + struct Bar bar1 = {}; + struct Baz baz1 = {"ZYX", "HI\0"}; + + // ok + struct Foo foo2 = foo1; + + // ok + struct Qux qux1 = { .foo = foo1, .bar = bar1, .baz = baz1 }; + + // ok + struct Qux qux2 = { .foo = foo1, /* .bar = ... */ .baz = baz1 }; + + // expected-error@+1{{array 'qux3.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'X'; got 0}} + struct Qux qux3 = { .foo = foo1, .bar = bar1 /* .baz = ... */ }; + + // ok + foo_as_param(foo1); + + // ok + return foo1; +} + +struct Quux copy_init2(void) { + struct Quux quux1 = { "Hello world" }; + + // ok + struct Quux quux2 = quux1; + + // ok + quux_as_param(quux1); + + // ok + return quux1; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c b/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c new file mode 100644 index 0000000000000..bd3465fd50115 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int a[__null_terminated 2]; + int b[__terminated_by(42) 2]; +}; + +struct Bar { + int a[__null_terminated 2]; +}; + +struct Baz { + struct Foo f; + struct Bar b; +}; + +// ok +int g_a[__null_terminated 8]; + +// expected-error@+1{{array 'g_b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} +int g_b[__terminated_by(42) 8]; + +// expected-error@+1{{array 'g_foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} +struct Foo g_foo; + +// ok +struct Bar g_bar; + +// expected-error@+1{{array 'g_baz.f.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} +struct Baz g_baz; + +void no_init_static_local(void) { + // ok + static int a[__null_terminated 8]; + + // expected-error@+1{{array 'b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + static int b[__terminated_by(42) 8]; + + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + static struct Foo foo; + + // ok + static struct Bar bar; + + // expected-error@+1{{array 'baz.f.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + static struct Baz baz; +} + +void no_init_local(void) { + // expected-error@+1{{array 'a' with '__terminated_by' attribute must be initialized}} + int a[__null_terminated 8]; + + // expected-error@+1{{array 'b' with '__terminated_by' attribute must be initialized}} + int b[__terminated_by(42) 8]; + + // expected-error@+2{{array 'foo.a' with '__terminated_by' attribute must be initialized}} + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute must be initialized}} + struct Foo foo; + + // expected-error@+1{{array 'bar.a' with '__terminated_by' attribute must be initialized}} + struct Bar bar; + + // expected-error@+3{{array 'baz.f.a' with '__terminated_by' attribute must be initialized}} + // expected-error@+2{{array 'baz.f.b' with '__terminated_by' attribute must be initialized}} + // expected-error@+1{{array 'baz.b.a' with '__terminated_by' attribute must be initialized}} + struct Baz baz; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-attr.c b/clang/test/BoundsSafety/Sema/terminated-by-attr.c new file mode 100644 index 0000000000000..e3e63e7df5411 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-attr.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Check if the terminator is an ICE. +void term(int val) { + char a1[__terminated_by(0)] = ""; + char a2[__terminated_by(0x40 + 1)] = {'A'}; + char a3[__terminated_by(val)] = ""; // expected-error{{'__terminated_by' attribute requires an integer constant}} +} + +void multiple_attrs(void) { + char a1[__null_terminated __null_terminated] = ""; // expected-warning{{array annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + char a2[__null_terminated __terminated_by(42)] = ""; // expected-error{{array cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '42'}} + char a3[__terminated_by(42)] = ""; // expected-error{{array 'a3' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 42; got 0)}} + + char *__null_terminated __null_terminated p1 = ""; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + char *__null_terminated __terminated_by(42) p2 = ""; // expected-error{{pointer cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '42'}} + char * __terminated_by(0) __terminated_by(1) __terminated_by(2) __terminated_by(0) p3 = ""; + // expected-error@-1 2{{pointer cannot have more than one terminator attribute}} + // expected-note@-2{{conflicting arguments for terminator were '0' and '1'}} + // expected-note@-3{{conflicting arguments for terminator were '0' and '2'}} + // expected-warning@-4{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + + char *__null_terminated a4[__null_terminated 1] = {0}; // ok (the attributes apply to different types) +} + +void type(int v) { + char __null_terminated c; // expected-error{{'__terminated_by' attribute can be applied to pointers, constant-length arrays or incomplete arrays}} + char a1[__null_terminated v]; // expected-error{{'__terminated_by' attribute can be applied to pointers, constant-length arrays or incomplete arrays}} + + float a2[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + float *__null_terminated p1; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + int(*__null_terminated p2)[1]; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + struct Foo { + int x; + }; + + struct Foo a3[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + struct Foo *__null_terminated p3; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + int *__single a4[__null_terminated 1] = {}; // ok + int *__single *__null_terminated p4 = 0; // ok + + int *__bidi_indexable a5[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + int *__bidi_indexable *__null_terminated p5; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} +} + +void ptr_attrs(void) { + char *__null_terminated __single p1 = ""; // ok + char *__single __null_terminated p2 = ""; // ok + + char *__null_terminated __indexable p3 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__indexable __null_terminated p4 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __bidi_indexable p5 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__bidi_indexable __null_terminated p6 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __unsafe_indexable p7 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__unsafe_indexable __null_terminated p8 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __counted_by(0) p9 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__counted_by(0) __null_terminated p10 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __sized_by(0) p11 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__sized_by(0) __null_terminated p12 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__sized_by(0) p13 = 0; + __typeof__(p13) __null_terminated p14 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c b/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c new file mode 100644 index 0000000000000..18710f6fbc44b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c @@ -0,0 +1,53 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __null_terminated should be inherited. +void inherit(const char *__null_terminated p) { + const char *__null_terminated q1 = (const char *)p; + const char *__null_terminated q2 = (char *)p; +} + +// __null_terminated shouldn't be inherited. +void dont_inherit(const char *__null_terminated p) { + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated q1 = (const int *)p; + + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated q2 = (int *)p; + + // expected-error@+1{{pointers with incompatible terminators casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char * __terminated_by(42)' (aka 'const char *')}} + const char *__null_terminated q3 = (const char *__terminated_by(42))p; + + // expected-error@+1{{casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + const char *__null_terminated q4 = (const char *__single)p; + + // expected-error@+3{{casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + const char *__null_terminated q5 = (const char *__bidi_indexable)p; +} + +void inherit_nested(const char *__null_terminated *__null_terminated p) { + const char *__null_terminated *__null_terminated q1 = (const char **)p; + const char *__null_terminated *__null_terminated q2 = (const char *__null_terminated *)p; + const char *__null_terminated *__null_terminated q3 = (const char **__null_terminated)p; +} + +void dont_inherit_nested(const char *__null_terminated *__null_terminated p) { + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') with an expression of incompatible type 'const int *__single*__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated *__null_terminated q1 = (const int **)p; + + // expected-error@+1{{pointers with incompatible terminators casting 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') to incompatible type 'const char * __terminated_by(42)*' (aka 'const char **')}} + const char *__null_terminated *__null_terminated q2 = (const char *__terminated_by(42) *)p; + + // expected-error@+1{{casting 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') to incompatible type 'const char *__single*' that discards '__terminated_by' attribute is not allowed}} + const char *__null_terminated *__null_terminated q3 = (const char *__single *)p; +} + +void test_terminated_by(const char *__terminated_by(8) p) { + // expected-error@+1{{casting 'const char *__single __terminated_by(8)' (aka 'const char *__single') to incompatible type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + const char *__bidi_indexable q = (const char *__bidi_indexable)p; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c b/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..e995b4822a1f8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int x; + int y; +}; + +struct Bar; + +static int array[42]; + +void term_ice(int *__indexable ptr, int val) { + __unsafe_terminated_by_from_indexable(0, ptr); // ok + __unsafe_terminated_by_from_indexable(42 * 1337, ptr); // ok + __unsafe_terminated_by_from_indexable(val, ptr); // expected-error{{terminator value is not a constant expression}} +} + +void ptr_type(int *__null_terminated tb, int *__single s, int *__indexable i, + int *__bidi_indexable bi, int *__unsafe_indexable ui, + int *__counted_by(len) cb, int len) { + __unsafe_null_terminated_from_indexable(array); // ok + __unsafe_null_terminated_from_indexable(tb); // ok (tb is a __single pointer) + __unsafe_null_terminated_from_indexable(s); // ok + __unsafe_null_terminated_from_indexable(i); // ok + __unsafe_null_terminated_from_indexable(bi); // ok + __unsafe_null_terminated_from_indexable(ui); // expected-error{{pointer argument must be a safe pointer ('int *__unsafe_indexable' invalid)}} + __unsafe_null_terminated_from_indexable(cb); // ok + __unsafe_null_terminated_from_indexable(*s); // expected-error{{pointer argument must be a safe pointer ('int' invalid)}} + __unsafe_null_terminated_from_indexable(0); // expected-error{{pointer argument must be a safe pointer ('int' invalid)}} +} + +void pointee_type(float *__indexable f, int *__indexable i, char *__indexable c, + struct Foo *__indexable foo, struct Bar *__indexable bar, + float **__indexable pf, int **__indexable pi, + char **__indexable pc, void **__indexable pv, + struct Foo **__indexable pfoo, + struct Bar **__indexable pbar, + int *__unsafe_indexable *__indexable pui, + int *__bidi_indexable *__indexable pbi) { + __unsafe_null_terminated_from_indexable(f); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(i); // ok + __unsafe_null_terminated_from_indexable(c); // ok + __unsafe_null_terminated_from_indexable(foo); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(bar); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(pf); // ok + __unsafe_null_terminated_from_indexable(pi); // ok + __unsafe_null_terminated_from_indexable(pc); // ok + __unsafe_null_terminated_from_indexable(pv); // ok + __unsafe_null_terminated_from_indexable(pfoo); // ok + __unsafe_null_terminated_from_indexable(pbar); // ok + __unsafe_null_terminated_from_indexable(pui); // ok + __unsafe_null_terminated_from_indexable(pbi); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} +} + +void ptr_to_term_type(int *__null_terminated tb, int *__single s, + int *__indexable i, int *__bidi_indexable bi, + int *__unsafe_indexable ui, int *__counted_by(len) cb, + int len) { + __unsafe_null_terminated_from_indexable(i, array); // ok + __unsafe_null_terminated_from_indexable(i, tb); // ok + __unsafe_null_terminated_from_indexable(i, s); // ok + __unsafe_null_terminated_from_indexable(i, i); // ok + __unsafe_null_terminated_from_indexable(i, bi); // ok + __unsafe_null_terminated_from_indexable(i, ui); // ok + __unsafe_null_terminated_from_indexable(i, cb); // ok + __unsafe_null_terminated_from_indexable(i, *s); // expected-error{{pointer to terminator argument must be a pointer ('int' invalid)}} + __unsafe_null_terminated_from_indexable(i, 0); // expected-error{{pointer to terminator argument must be a pointer ('int' invalid)}} +} + +void pointee_mismatch(float *__indexable f, int *__indexable i, + char *__indexable c, int **__indexable pi, + void **__indexable pv) { + __unsafe_null_terminated_from_indexable(array, array); // ok + __unsafe_null_terminated_from_indexable(i, f); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(i, i); // ok + __unsafe_null_terminated_from_indexable(i, c); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(i, pi); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(pi, pi); // ok + __unsafe_null_terminated_from_indexable(pv, pv); // ok + __unsafe_null_terminated_from_indexable(pi, pv); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c new file mode 100644 index 0000000000000..bfad5d36dac8e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(int *__null_terminated p, int v) { + p++; // ok + ++p; // ok + p--; // expected-error{{cannot decrement '__terminated_by' pointer 'p'}} + --p; // expected-error{{cannot decrement '__terminated_by' pointer 'p'}} + + p += 0; // ok + p -= 0; // ok + p += 1; // ok + p -= 1; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += 2 - 1; // ok + p += 1 - 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= 1 - 2; // ok + p -= 2 - 1; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += v; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= v; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + + (void)(p + 0); // ok + (void)(p - 0); // ok + (void)(p + 1); // ok + (void)(p - 1); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + 2); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - 2); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + (2 - 1)); // ok + (void)(p + (1 - 2)); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - (1 - 2)); // ok + (void)(p - (2 - 1)); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + v); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - v); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + + (void)p[0]; // ok + (void)p[1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[-1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[1 - 1]; // ok + (void)p[2 - 1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[1 - 2]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[v]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c new file mode 100644 index 0000000000000..f712940b932b5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void unsafe_char(const char *__unsafe_indexable); + +void unsafe_int(const int *__unsafe_indexable); + +// expected-note@+1{{passing argument to parameter here}} +void nul_char(const char *__null_terminated); + +// expected-note@+1{{passing argument to parameter here}} +void x_char(const char *__terminated_by('X')); + +// expected-note@+1{{passing argument to parameter here}} +void nul_int(const int *__null_terminated); + +// __null_terminated char pointer <-> __unsafe_indexable char pointer + +char *__null_terminated unsafe_indexable_to_null_terminated_char(char *__unsafe_indexable p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + char *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__unsafe_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul_char(p); + + // expected-error@+1{{returning 'char *__unsafe_indexable' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__unsafe_indexable' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((char *__null_terminated)p); +} + +char *__unsafe_indexable null_terminated_char_to_unsafe_indexable(char *__null_terminated p) { + // ok + char *__unsafe_indexable q = p; + + // ok + q = p; + + // ok + unsafe_char(p); + + // ok + return p; + + // ok + (void)((char *__unsafe_indexable)p); +} + +// __terminated_by('X') char pointer <-> __unsafe_indexable char pointer + +char *__terminated_by('X') unsafe_indexable_to_terminated_by_char(char *__unsafe_indexable p) { + // expected-error@+1{{initializing 'char *__single __terminated_by('X')' (aka 'char *__single') with an expression of incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + char *__terminated_by('X') q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by('X')' (aka 'char *__single') from incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__unsafe_indexable' to parameter of incompatible type 'const char *__single __terminated_by('X')' (aka 'const char *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + x_char(p); + + // expected-error@+1{{returning 'char *__unsafe_indexable' from a function with incompatible result type 'char *__single __terminated_by('X')' (aka 'char *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__unsafe_indexable' to incompatible type 'char * __terminated_by('X')' (aka 'char *') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + (void)((char *__terminated_by('X'))p); +} + +char *__unsafe_indexable terminated_by_char_to_unsafe_indexable(char *__terminated_by('X') p) { + char *__unsafe_indexable q = p; + + q = p; + + unsafe_char(p); + + return p; + + (void)((char *__unsafe_indexable)p); +} + +// __null_terminated int pointer <-> __unsafe_indexable int pointer + +int *__null_terminated unsafe_indexable_to_null_terminated_int(int *__unsafe_indexable p) { + // expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + int *__null_terminated q = p; + + // expected-error@+1{{assigning to 'int *__single __terminated_by(0)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul_int(p); + + // expected-error@+1{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'int *__unsafe_indexable' to incompatible type 'int * __terminated_by(0)' (aka 'int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((int *__null_terminated)p); +} + +int *__unsafe_indexable null_terminated_int_to_unsafe_indexable(int *__null_terminated p) { + int *__unsafe_indexable q = p; + + q = p; + + unsafe_int(p); + + return p; + + (void)((int *__unsafe_indexable)p); +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c new file mode 100644 index 0000000000000..ad1cbff55e909 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c @@ -0,0 +1,798 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1 +{{passing argument to parameter here}} +void nul(const char *__null_terminated); + +void nul_c(const char *const __null_terminated); + +// expected-note@+1 +{{passing argument to parameter here}} +void nul_int(const int *__null_terminated); + +// expected-note@+1{{passing argument to parameter here}} +void _42(const char *__terminated_by(42)); + +// expected-note@+1{{passing argument to parameter here}} +void _42_int(const int *__terminated_by(42)); + +// expected-note@+1 +{{passing argument to parameter here}} +void nul_nested(char *__null_terminated *__null_terminated); + +void _42_nested(char *__terminated_by(42) * __null_terminated); + +// Null + +char *__null_terminated null(void) { + char *__null_terminated p = 0; // ok + p = 0; // ok + nul(0); // ok + return 0; // ok + (void)((char *__null_terminated)0); // ok +} + +// Char arrays +const char *__null_terminated const_arr_stringlit(int x) { + const char const_arr_stringlit[] = "hello"; + + const char *__null_terminated p = const_arr_stringlit; // ok + p = const_arr_stringlit; // ok + nul(const_arr_stringlit); // ok + (void)((const char *__null_terminated) const_arr_stringlit); // ok + return const_arr_stringlit; // ok +} + +const char *__null_terminated const_arr_stringlit_arit(int x) { + const char const_arr_stringlit[] = "hello"; + + const char *__null_terminated p = const_arr_stringlit + 3; // ok + p = const_arr_stringlit + 3; // ok + nul(const_arr_stringlit + 3); // ok + (void)((const char *__null_terminated)( const_arr_stringlit + 3)); // ok + return const_arr_stringlit + 3; // ok +} + +const char *__null_terminated const_arr_stringlit_arit_end(int x) { + const char const_arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit + 6; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit + 6; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit + 6); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated)( const_arr_stringlit + 6)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit + 6; +} + +const char *__null_terminated const_arr_stringlit_arit_pastend(int x) { + const char const_arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit + 7; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit + 7; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit + 7); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated)( const_arr_stringlit + 7)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit + 7; +} + +const char *__null_terminated const_arr_braced(int x) { + const char const_arr_braced[] = "hello"; + + const char *__null_terminated p = const_arr_braced; // ok + p = const_arr_braced; // ok + nul(const_arr_braced); // ok + (void)((const char *__null_terminated) const_arr_braced); // ok + return const_arr_braced; // ok +} + +const char *__null_terminated const_arr_stringlit_nonnt_trunc(int x) { + const char const_arr_stringlit_nonnt[2] = "he"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit_nonnt; + // expected-error@+3{{passing 'const char[2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_stringlit_nonnt); + // expected-error@+3{{returning 'const char[2]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit_nonnt; +} + +const char *__null_terminated const_arr_braced_nonnt_trunc(int x) { + const char const_arr_braced_nonnt[2] = {'h', 'e'}; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_braced_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_braced_nonnt; + // expected-error@+3{{passing 'const char[2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_braced_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_braced_nonnt); + // expected-error@+3{{returning 'const char[2]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_braced_nonnt; +} + +const char *__null_terminated const_arr_stringlit_nt(int x) { + const char const_arr_stringlit_nt[3] = "he"; + + const char *__null_terminated p = const_arr_stringlit_nt; + p = const_arr_stringlit_nt; + nul(const_arr_stringlit_nt); + (void)((const char *__null_terminated) const_arr_stringlit_nt); + return const_arr_stringlit_nt; +} + +const char *__null_terminated const_arr_braced_implicit_nt(int x) { + const char const_arr_braced_implicit_nt[3] = {'h', 'e'}; + + const char *__null_terminated p = const_arr_braced_implicit_nt; + p = const_arr_braced_implicit_nt; + nul(const_arr_braced_implicit_nt); + (void)((const char *__null_terminated) const_arr_braced_implicit_nt); + return const_arr_braced_implicit_nt; +} + +const char *__null_terminated const_arr_stringlit_nt_oversized(int x) { + const char const_arr_stringlit_nt_oversized[4] = "he"; + + const char *__null_terminated p = const_arr_stringlit_nt_oversized; + p = const_arr_stringlit_nt_oversized; + nul(const_arr_stringlit_nt_oversized); + (void)((const char *__null_terminated) const_arr_stringlit_nt_oversized); + return const_arr_stringlit_nt_oversized; +} + +const char *__null_terminated const_arr_braced_nonnt_oversized(int x) { + const char const_arr_braced_nonnt[4] = {'h', 'e', '\0', 'w'}; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[4]' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_braced_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[4]' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_braced_nonnt; + // expected-error@+3{{passing 'const char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_braced_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_braced_nonnt); + // expected-error@+3{{returning 'const char[4]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_braced_nonnt; +} + +const char *__null_terminated arr_stringlit(int x) { + // expected-note@+1 4{{consider adding 'const' to 'arr_stringlit'}} + char arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'char[6]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = arr_stringlit; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'char[6]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = arr_stringlit; + // expected-error@+3{{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(arr_stringlit); + // expected-error@+3{{casting 'char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) arr_stringlit); + // expected-error@+3{{returning 'char[6]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return arr_stringlit; +} + +// Compound literal + +const int *__null_terminated compound_lit_explicit_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2, 0}; // ok + p = (const int[3]){1, 2, 0}; // ok + nul_int((const int[3]){1, 2, 0}); // ok + (void)((const int *__null_terminated) (const int[3]){1, 2, 0}); // ok + return (const int[3]){1, 2, 0}; // ok +} + +const int *__null_terminated compound_lit_explicit_bad(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int[3]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3}; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int[3]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3}; + // expected-error@+3{{passing 'const int[3]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3}); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) (const int[3]){1, 2, 3}); + // expected-error@+3{{returning 'const int[3]' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3}; +} + +const int *__null_terminated compound_lit_implicit_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2}; // ok + p = (const int[3]){1, 2}; // ok + nul_int((const int[3]){1, 2}); // ok + (void)((const int *__null_terminated) (const int[3]){1, 2}); // ok + return (const int[3]){1, 2}; // ok +} + +const int *__terminated_by(42) compound_lit_implicit_bad(void) { + // expected-error@+1{{initializing 'const int *__single __terminated_by(42)' (aka 'const int *__single') with an expression of incompatible type 'const int[3]' is an unsafe operation}} + const int *__terminated_by(42) p = (const int[3]){1, 2}; + // expected-error@+1{{assigning to 'const int *__single __terminated_by(42)' (aka 'const int *__single') from incompatible type 'const int[3]' is an unsafe operation}} + p = (const int[3]){1, 2}; + // expected-error@+1{{passing 'const int[3]' to parameter of incompatible type 'const int *__single __terminated_by(42)' (aka 'const int *__single') is an unsafe operation}} + _42_int((const int[3]){1, 2}); + // expected-error@+1{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(42)' (aka 'const int *') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + (void)((const int *__terminated_by(42)) (const int[3]){1, 2}); + // expected-error@+1{{returning 'const int[3]' from a function with incompatible result type 'const int *__single __terminated_by(42)' (aka 'const int *__single') is an unsafe operation}} + return (const int[3]){1, 2}; +} + +const int *__null_terminated compound_lit_arith_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2, 0} + 2; // ok + p = (const int[3]){1, 2, 0} + 2; // ok + nul_int((const int[3]){1, 2, 0} + 2); // ok + (void)((const int *__null_terminated) ((const int[3]){1, 2, 0} + 2)); // ok + return (const int[3]){1, 2, 0} + 2; // ok +} + +const int *__null_terminated compound_lit_arith_end(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} + 3; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} + 3; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} + 3); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} + 3)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} + 3; +} + +const int *__null_terminated compound_lit_arith_past_end(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} + 4; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} + 4; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} + 4); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} + 4)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} + 4; +} + +const int *__null_terminated compound_lit_arith_neg(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} - 1; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} - 1; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} - 1); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} - 1)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} - 1; +} + +// String literal + +char *__null_terminated string_literal_nul(void) { + char *__null_terminated p = "init"; // ok + p = "assign"; // ok + nul("passing"); // ok + return "returning"; // ok + (void)((const char *__null_terminated) "casting"); // ok +} + +char *__terminated_by(42) string_literal_42(void) { + char *__terminated_by(42) p = "init"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + p = "assign"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + _42("passing"); // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + return "returning"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + (void)((const char *__terminated_by(42)) "casting"); +} + +// Terminators + +char *__null_terminated terminators(char *__terminated_by(42) p, char *__null_terminated q) { + // expected-error@+1{{pointers with incompatible terminators initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single __terminated_by(42)' (aka 'char *__single')}} + char *__null_terminated a = p; + + // ok + char *__null_terminated b = q; + + // expected-error@+1{{pointers with incompatible terminators assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__single __terminated_by(42)' (aka 'char *__single')}} + a = p; + + // ok + a = q; + + // expected-error@+1{{pointers with incompatible terminators passing 'char *__single __terminated_by(42)' (aka 'char *__single') to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + nul(p); + + // ok + _42(p); + + // expected-error@+1{{pointers with incompatible terminators returning 'char *__single __terminated_by(42)' (aka 'char *__single') from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single')}} + return p; + + // ok + return q; + + // expected-error@+1{{pointers with incompatible terminators casting 'char *__single __terminated_by(42)' (aka 'char *__single') to incompatible type 'char * __terminated_by(0)' (aka 'char *')}} + (void)((char *__null_terminated)p); + + // ok + (void)((char *__null_terminated)q); +} + +char *__null_terminated *__null_terminated nested_terminators(char *__terminated_by(42) * __null_terminated p, char *__null_terminated *__null_terminated q) { + // expected-error@+1{{pointers with incompatible terminators initializing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + char *__null_terminated *__null_terminated a = p; + + // ok + char *__null_terminated *__null_terminated b = q; + + // expected-error@+1{{pointers with incompatible terminators assigning to 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + a = p; + + // ok + a = q; + + // expected-error@+1{{pointers with incompatible terminators passing 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + nul_nested(p); + + // ok + _42_nested(p); + + // expected-error@+1{{pointers with incompatible terminators returning 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + return p; + + // ok + return q; + + // expected-error@+1{{pointers with incompatible terminators casting 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char * __terminated_by(0)* __terminated_by(0)' (aka 'char **')}} + (void)((char *__null_terminated *__null_terminated)p); + + // ok + (void)((char *__null_terminated *__null_terminated)q); +} + +// Quals + +char *__null_terminated quals_c_to_nc(char *const __null_terminated p) { + // ok + char *__null_terminated a = p; + + // ok + a = p; + + // ok + nul(p); + + // ok + (void)((char *__null_terminated)p); + + // ok + return p; +} + +char *const __null_terminated quals_nc_to_c(char *__null_terminated p) { + // ok + char *const __null_terminated a = p; + + // expected-note@-2{{variable 'a' declared const here}} + // expected-error@+1{{cannot assign to variable 'a' with const-qualified type 'char *__single __terminated_by(0)const' (aka 'char *__singleconst')}} + a = p; + + // ok + nul_c(p); + + // ok + (void)((char *const __null_terminated)p); + + // ok + return p; +} + +// Non-TerminatedBy to TerminatedBy + +char *__null_terminated single_to_terminated_by(char *__single p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + char *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul(p); + + // expected-error@+1{{returning 'char *__single' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__single' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((char *__null_terminated)p); +} + +char *__null_terminated indexable_to_terminated_by(char *__indexable p) { + // expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + char *__null_terminated q = p; + + // expected-error@+3{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + q = p; + + // expected-error@+3{{passing 'char *__indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(p); + + // expected-error@+3{{returning 'char *__indexable' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return p; + + // expected-error@+3{{casting 'char *__indexable' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((char *__null_terminated)p); +} + +char *__null_terminated *__null_terminated nested_to_terminated_by(char *__single *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + char *__null_terminated *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + q = p; + + // expected-error@+1{{passing 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + nul_nested(p); + + // expected-error@+1{{returning 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + return p; + + // expected-error@+1{{casting 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char * __terminated_by(0)* __terminated_by(0)' (aka 'char **') that adds '__terminated_by' attribute is not allowed}} + (void)((char *__null_terminated *__null_terminated)p); +} + +// TerminatedBy to Non-TerminatedBy + +// expected-note@+1{{passing argument to parameter here}} +void foo_single(char *__single); + +// expected-note@+1{{passing argument to parameter here}} +void foo_indexable(char *__indexable); + +char *__single single_from_terminated_by(char *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single' with an expression of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + char *__single q = p; + + // expected-error@+1{{assigning to 'char *__single' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + q = p; + + // expected-error@+1{{passing 'char *__single __terminated_by(0)' (aka 'char *__single') to parameter of incompatible type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + foo_single(p); + + // expected-error@+1{{returning 'char *__single __terminated_by(0)' (aka 'char *__single') from a function with incompatible result type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + return p; + + // expected-error@+1{{casting 'char *__single __terminated_by(0)' (aka 'char *__single') to incompatible type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + (void)((char *__single)p); +} + +char *__indexable indexable_from_terminated_by(char *__null_terminated p) { + // expected-error@+3{{initializing 'char *__indexable' with an expression of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + char *__indexable q = p; + + // expected-error@+3{{assigning to 'char *__indexable' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + q = p; + + // expected-error@+3{{passing 'char *__single __terminated_by(0)' (aka 'char *__single') to parameter of incompatible type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + foo_indexable(p); + + // expected-error@+3{{returning 'char *__single __terminated_by(0)' (aka 'char *__single') from a function with incompatible result type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + return p; + + // expected-error@+3{{casting 'char *__single __terminated_by(0)' (aka 'char *__single') to incompatible type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + (void)((char *__indexable)p); +} + +// expected-note@+1{{passing argument to parameter here}} +void bar(char *__single *__null_terminated); + +char *__single *__null_terminated nested_from_terminated_by(char *__null_terminated *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + char *__single *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + q = p; + + // expected-error@+1{{passing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + bar(p); + + // expected-error@+1{{returning 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + return p; + + // expected-error@+1{{casting 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char *__single* __terminated_by(0)' (aka 'char *__single*') that discards '__terminated_by' attribute is not allowed}} + (void)((char *__single *__null_terminated)p); +} + +void sign_mismatch(void) { + const unsigned char array[] = "foo"; + // expected-warning@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of type 'const unsigned char[4]' converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + const char *__terminated_by(0) p = array; +} + +// Conditional operators + +char *__null_terminated cond_op_string_lit(int cond) { + char *__null_terminated p = cond ? "init-true" : "init-false"; // ok + p = cond ? "assign-true" : "assign-false"; // ok + nul(cond ? "passing-true" : "passing-false"); // ok + (void)((const char *__null_terminated) (cond ? "casting-true" : "casting-false")); // ok + return cond ? "returning-true" : "returning-false"; // ok +} + +char *__null_terminated cond_op_nt_with_string_lit(int cond, char *__null_terminated s) { + char *__null_terminated p = cond ? s : "init-false"; // ok + p = cond ? s : "assign-false"; // ok + nul(cond ? s : "passing-false"); // ok + (void)((const char *__null_terminated) (cond ? s : "casting-false")); // ok + return cond ? s : "returning-false"; // ok +} + +char *__null_terminated cond_op_string_lit_nested(int c, int d, int e) { + char *__null_terminated p = c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok + p = c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok + nul(c ? (d ? "tt" : "tf") : (e ? "ft" : "ff")); // ok + (void)((const char *__null_terminated)(c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"))); // ok + return c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok +} + +const char *__null_terminated cond_op_char_array(int cond) { + const char t[] = "true"; + const char f[] = "false"; + const char *__null_terminated p = cond ? t : f; // ok + p = cond ? t : f; // ok + nul(cond ? t : f); // ok + (void)((const char *__null_terminated) (cond ? t : f)); // ok + return cond ? t : f; // ok +} + +const char *__null_terminated cond_op_char_array_arith(int cond) { + const char t[] = "true"; + const char f[] = "false"; + const char *__null_terminated p = cond ? t+3 : f+4; // ok + p = cond ? t+3 : f+4; // ok + nul(cond ? t+3 : f+4); // ok + (void)((const char *__null_terminated) (cond ? t+3 : f+4)); // ok + return cond ? t+3 : f+4; // ok +} + +const char *__null_terminated cond_op_char_array_arith_true_end(int cond) { + const char t[] = "true"; + const char f[] = "false"; + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t+5 : f+4; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t+5 : f+4; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t+5 : f+4); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t+5 : f+4)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t+5 : f+4; +} + +const char *__null_terminated cond_op_char_array_arith_false_end(int cond) { + const char t[] = "true"; + const char f[] = "false"; + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t+4 : f+6; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t+4 : f+6; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t+4 : f+6); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t+4 : f+6)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t+4 : f+6; +} + +const char *__null_terminated cond_op_char_array_wrong_term(int cond) { + const char t[] = "true"; + const char f[5] = "false"; // not NUL-terminated + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t : f; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t : f; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t : f); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t : f)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t : f; +} + +const int *__null_terminated cond_op_different_type(int cond) { + const int i[] = {1, 2, 3, 0}; + const long l[] = {1, 2, 0}; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + const long *__null_terminated p = cond ? i : l; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + p = cond ? i : l; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + nul_int(cond ? i : l); + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + (void)((const int *__null_terminated) (cond ? i : l)); + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + return cond ? i : l; +} + +char *__null_terminated gnu_cond_op_string_lit_true(void) { + char *__null_terminated p = "t" ?: "f"; // ok + p = "t" ?: "f"; // ok + nul("t" ?: "f"); // ok + (void)((const char *__null_terminated) ("t" ?: "f")); // ok + return "t" ?: "f"; // ok +} + +char *__null_terminated gnu_cond_op_string_lit_false(void) { + char *__null_terminated p = 0 ?: "f"; // ok + p = 0 ?: "f"; // ok + nul(0 ?: "f"); // ok + (void)((const char *__null_terminated) (0 ?: "f")); // ok + return 0 ?: "f"; // ok +} + +void const_ptr_indexable_to_terminated_by(int x) { + const char *__indexable ptr_terminated_by;; + + // expected-error@+1{{initializing 'const char *__single __terminated_by(4)' (aka 'const char *__single') with an expression of incompatible type 'const char *__indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + const char *__terminated_by(4) p = ptr_terminated_by; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c new file mode 100644 index 0000000000000..7a66ff3a1e959 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=strict,both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=relaxed,both -Wno-error=bounds-safety-strict-terminated-by-cast -Wno-error %s + +#include + +// The relaxed case should be removed by rdar://118390724 + +void foo(const char * __null_terminated); // both-note{{passing argument to parameter here}} +void bar(const char * __null_terminated * __single); // both-note{{passing argument to parameter here}} + +void test(const char * __single sp) { + // strict-error@+2{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()}} + // relaxed-warning@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()}} + const char * __null_terminated ntp = sp; + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char * __null_terminated ntp2 = (const char * __null_terminated) sp; + + // strict-error@+2{{passing 'const char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{passing 'const char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + foo(sp); + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + foo((const char * __null_terminated) sp); + + const char * __null_terminated ntp3 = ntp; + // strict-error@+2{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + ntp3 = sp; + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + ntp3 = (const char * __null_terminated) sp; + + + /* --- Nested --- */ + + const char * __single * __single spp = &sp; + // strict-error@+2{{initializing 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') with an expression of incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{initializing 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') with an expression of incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + const char * __null_terminated * __single ntpp = spp; + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + const char * __null_terminated * __single ntpp2 = (const char * __null_terminated * __single) spp; + + // strict-error@+2{{passing 'const char *__single*__single' to parameter of incompatible type 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{passing 'const char *__single*__single' to parameter of incompatible type 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + bar(spp); + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + bar((const char * __null_terminated * __single) spp); + + const char * __null_terminated * __single ntpp3 = ntpp; + // strict-error@+2{{assigning to 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') from incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{assigning to 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') from incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + ntpp3 = spp; + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + ntpp3 = (const char * __null_terminated * __single) spp; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c new file mode 100644 index 0000000000000..8d73477bfa0e2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +struct Foo { + int *__null_terminated a; + int *__terminated_by(42) b; +}; + +struct Bar { + int *__null_terminated a; +}; + +struct Baz { + struct Foo f; + struct Bar b; +}; + +int *__null_terminated g_a; // ok +int *__null_terminated g_b; // ok +struct Foo g_foo; // ok +struct Bar g_bar; // ok +struct Baz g_baz; // ok + +void no_init_static_local(void) { + static int *__null_terminated a; // ok + static int *__null_terminated b; // ok + static struct Foo foo; // ok + static struct Bar bar; // ok + static struct Baz baz; // ok +} + +void no_init_local(void) { + int *__null_terminated a; // ok + int *__null_terminated b; // ok + struct Foo foo; // ok + struct Bar bar; // ok + struct Baz baz; // ok +} + +void empty_init_local(void) { + struct Foo foo = {}; // ok + struct Bar bar = {}; // ok + struct Baz baz = {}; // ok +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-redecl.c b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c new file mode 100644 index 0000000000000..847a44df68dd2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{previous declaration is here}} +char *test(); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +char *__null_terminated test(); + +// expected-note@+1{{previous declaration is here}} +const char *test2(); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +const char *__terminated_by(-1) test2(); + +const char *test3(); +const char *__null_terminated test3(); + +// expected-note@+1{{previous declaration is here}} +void test4(int *__null_terminated arg); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test4(int *arg); + +// expected-note@+1{{previous declaration is here}} +void test5(int *__null_terminated arg); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test5(int *__terminated_by(-1) arg); + +// expected-note@+1{{previous declaration is here}} +void test6(int *__counted_by(len) buf, int len); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test6(int *__null_terminated buf, int len); + +// expected-note@+1{{previous declaration is here}} +void test7(int *__null_terminated buf, int len); +// expected-error@+1{{conflicting '__sized_by' attribute with the previous function declaration}} +void test7(int *__sized_by(len) buf, int len); + +// expected-note@+1{{previous declaration is here}} +void test8(int *__null_terminated buf, int len); +// expected-error@+1{{conflicting '__ended_by' attribute with the previous function declaration}} +void test8(int *__ended_by(end) buf, int *end); + +// expected-note@+1{{previous declaration is here}} +void test9(int *__ended_by(end) buf, int *end); +// expected-error@+1{{conflicting '__ended_by' attribute with the previous function declaration}} +void test9(int *buf, int *__null_terminated end); + +#include "terminated-by-redecl.h" + +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test_system_nt_argument(int *p); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +int *test_system_nt_return(); diff --git a/clang/test/BoundsSafety/Sema/terminated-by-redecl.h b/clang/test/BoundsSafety/Sema/terminated-by-redecl.h new file mode 100644 index 0000000000000..f436aa769f9de --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-redecl.h @@ -0,0 +1,12 @@ +#pragma clang system_header + +#include + +// expected-note@+1{{previous declaration is here}} +void test_system_nt_argument(int *__null_terminated p); + +// expected-note@+1{{previous declaration is here}} +int *__null_terminated test_system_nt_return(); + +void test_system_nt_argument_impl(int *__null_terminated p); +void test_system_nt_argument_impl(int *p); diff --git a/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c b/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c new file mode 100644 index 0000000000000..0c0bc419f5546 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +static int array[42]; +static int nt_array[__null_terminated 42]; + +void ptr_type(int *__null_terminated tb, int *__single s, int *__bidi_indexable bi) { + __terminated_by_to_indexable(array); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __terminated_by_to_indexable(nt_array); // ok (the array decays to a __single __null_terminated pointer) + + __terminated_by_to_indexable(tb); // ok + __terminated_by_to_indexable(s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__single' invalid)}} + __terminated_by_to_indexable(bi); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __terminated_by_to_indexable(*s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} + __terminated_by_to_indexable(0); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} +} + +void unsafe_ptr_type(int *__null_terminated tb, int *__single s, int *__bidi_indexable bi) { + __unsafe_terminated_by_to_indexable(array); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __unsafe_terminated_by_to_indexable(nt_array); // ok (the array decays to a __single __null_terminated pointer) + + __unsafe_terminated_by_to_indexable(tb); // ok + __unsafe_terminated_by_to_indexable(s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__single' invalid)}} + __unsafe_terminated_by_to_indexable(bi); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __unsafe_terminated_by_to_indexable(*s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} + __unsafe_terminated_by_to_indexable(0); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} +} + +void term_ice(int *__null_terminated p, int val) { + (void)__builtin_terminated_by_to_indexable(p, 0); // ok + (void)__builtin_terminated_by_to_indexable(p, 42 * 0); // ok + (void)__builtin_terminated_by_to_indexable(p, val); // expected-error{{terminator value is not a constant expression}} +} + +void unsafe_term_ice(int *__null_terminated p, int val) { + (void)__builtin_unsafe_terminated_by_to_indexable(p, 0); // ok + (void)__builtin_unsafe_terminated_by_to_indexable(p, 42 * 0); // ok + (void)__builtin_unsafe_terminated_by_to_indexable(p, val); // expected-error{{terminator value is not a constant expression}} +} + +void null(int *__null_terminated p, int *__terminated_by(42) q) { + __null_terminated_to_indexable(p); // ok + __null_terminated_to_indexable(q); // expected-error{{pointer argument must be terminated by 0 (got 42)}} +} + +void unsafe_null(int *__null_terminated p, int *__terminated_by(42) q) { + __unsafe_null_terminated_to_indexable(p); // ok + __unsafe_null_terminated_to_indexable(q); // expected-error{{pointer argument must be terminated by 0 (got 42)}} +} + +void _42(int *__null_terminated p, int *__terminated_by(42) q) { + (void)__builtin_terminated_by_to_indexable(p, 42); // expected-error{{pointer argument must be terminated by 42 (got 0)}} + (void)__builtin_terminated_by_to_indexable(q, 42); // ok +} + +void unsafe_42(int *__null_terminated p, int *__terminated_by(42) q) { + (void)__builtin_unsafe_terminated_by_to_indexable(p, 42); // expected-error{{pointer argument must be terminated by 42 (got 0)}} + (void)__builtin_unsafe_terminated_by_to_indexable(q, 42); // ok +} diff --git a/clang/test/BoundsSafety/Sema/ternary-on-indexables.c b/clang/test/BoundsSafety/Sema/ternary-on-indexables.c new file mode 100644 index 0000000000000..4ae0b07bb2c94 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ternary-on-indexables.c @@ -0,0 +1,348 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + +void Test(int sel) { + int a; + int *x = &a; + int *y = &a; + int *z = sel ? x : y; + + char c; + char *x_char = &c; + char *y_char = &c; + char *z_char = sel ? x_char : y_char; + + void *__indexable x_ix_void = &a; + void *__indexable y_ix_void = &a; + void *__indexable z_ix_void = y ?: y; + + int *__indexable x_ix = &a; + int *__indexable y_ix = &a; + int *__indexable z_ix = sel ? x_ix : y_ix; + + int *__single x_sg = &a; + int *__single y_sg = &a; + int *__single z_sg = sel ? x_sg : y_sg; + + int *__unsafe_indexable x_uix = &a; + int *__unsafe_indexable y_uix = &a; + int *__unsafe_indexable z_uix = sel ? x_uix : y_uix; + + char *__unsafe_indexable x_uix_char = &c; + char *__unsafe_indexable y_uix_char = &c; + char *__unsafe_indexable z_uix_char = sel ? x_uix_char : y_uix_char; + + z = sel ? x : y; + z = sel ? x : y_ix; + z = sel ? x : y_sg; + z = sel ? x : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x : y_ix_void; + + z = sel ? x_ix : y; + z = sel ? x_ix : y_ix; + z = sel ? x_ix : y_sg; + z = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_ix : y_ix_void; + + z = sel ? x_sg : y; + z = sel ? x_sg : y_ix; + z = sel ? x_sg : y_sg; + z = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_sg : y_ix_void; + + z = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z = sel ? x_ix_void : y; + z = sel ? x_ix_void : y_ix; + z = sel ? x_ix_void : y_sg; + z = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_ix_void : y_ix_void; + + z_ix = sel ? x : y; + z_ix = sel ? x : y_ix; + z_ix = sel ? x : y_sg; + z_ix = sel ? x : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x : y_ix_void; + + z_ix = sel ? x_ix : y; + z_ix = sel ? x_ix : y_ix; + z_ix = sel ? x_ix : y_sg; + z_ix = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_ix : y_ix_void; + + z_ix = sel ? x_sg : y; + z_ix = sel ? x_sg : y_ix; + z_ix = sel ? x_sg : y_sg; + z_ix = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_sg : y_ix_void; + + z_ix = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix = sel ? x_ix_void : y; + z_ix = sel ? x_ix_void : y_ix; + z_ix = sel ? x_ix_void : y_sg; + z_ix = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_ix_void : y_ix_void; + + z_sg = sel ? x_ix : y; + z_sg = sel ? x_ix : y_ix; + z_sg = sel ? x_ix : y_sg; + z_sg = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_ix : y_ix_void; + + z_sg = sel ? x_sg : y; + z_sg = sel ? x_sg : y_ix; + z_sg = sel ? x_sg : y_sg; + z_sg = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_sg : y_ix_void; + + z_sg = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_sg = sel ? x_ix_void : y; + z_sg = sel ? x_ix_void : y_ix; + z_sg = sel ? x_ix_void : y_sg; + z_sg = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_ix_void : y_ix_void; + + z_uix = sel ? x_ix : y; + z_uix = sel ? x_ix : y_ix; + z_uix = sel ? x_ix : y_sg; + z_uix = sel ? x_ix : y_uix; + z_uix = sel ? x_ix : y_ix_void; + + z_uix = sel ? x_sg : y; + z_uix = sel ? x_sg : y_ix; + z_uix = sel ? x_sg : y_sg; + z_uix = sel ? x_sg : y_uix; + z_uix = sel ? x_sg : y_ix_void; + + z_uix = sel ? x_uix : y; + z_uix = sel ? x_uix : y_ix; + z_uix = sel ? x_uix : y_sg; + z_uix = sel ? x_uix : y_uix; + z_uix = sel ? x_uix : y_ix_void; + + z_uix = sel ? x_ix_void : y; + z_uix = sel ? x_ix_void : y_ix; + z_uix = sel ? x_ix_void : y_sg; + z_uix = sel ? x_ix_void : y_uix; + z_uix = sel ? x_ix_void : y_ix_void; + + z_ix_void = sel ? x_ix : y; + z_ix_void = sel ? x_ix : y_ix; + z_ix_void = sel ? x_ix : y_sg; + z_ix_void = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_ix : y_ix_void; + + z_ix_void = sel ? x_sg : y; + z_ix_void = sel ? x_sg : y_ix; + z_ix_void = sel ? x_sg : y_sg; + z_ix_void = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_sg : y_ix_void; + + z_ix_void = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix_void = sel ? x_ix_void : y; + z_ix_void = sel ? x_ix_void : y_ix; + z_ix_void = sel ? x_ix_void : y_sg; + z_ix_void = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_ix : y; + + z_char = sel ? x_ix : y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_ix : y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_ix : y_ix_void; + + z_char = sel ? x_sg : y; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_sg : y_ix_void; + + z_char = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_char = sel ? x_ix_void : y; + z_char = sel ? x_ix_void : y_ix; + z_char = sel ? x_ix_void : y_sg; + z_char = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_ix_void : y_ix_void; + + z = x ?: y; + z = x ?: y_ix; + z = x ?: y_sg; + z = x ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x ?: y_ix_void; + + z = x_ix ?: y; + z = x_ix ?: y_ix; + z = x_ix ?: y_sg; + z = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_ix ?: y_ix_void; + + z = x_sg ?: y; + z = x_sg ?: y_ix; + z = x_sg ?: y_sg; + z = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_sg ?: y_ix_void; + + z = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z = x_ix_void ?: y; + z = x_ix_void ?: y_ix; + z = x_ix_void ?: y_sg; + z = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_ix_void ?: y_ix_void; + + z_ix = x ?: y; + z_ix = x ?: y_ix; + z_ix = x ?: y_sg; + z_ix = x ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x ?: y_ix_void; + + z_ix = x_ix ?: y; + z_ix = x_ix ?: y_ix; + z_ix = x_ix ?: y_sg; + z_ix = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_ix ?: y_ix_void; + + z_ix = x_sg ?: y; + z_ix = x_sg ?: y_ix; + z_ix = x_sg ?: y_sg; + z_ix = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_sg ?: y_ix_void; + + z_ix = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix = x_ix_void ?: y; + z_ix = x_ix_void ?: y_ix; + z_ix = x_ix_void ?: y_sg; + z_ix = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_ix_void ?: y_ix_void; + + z_sg = x_ix ?: y; + z_sg = x_ix ?: y_ix; + z_sg = x_ix ?: y_sg; + z_sg = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_ix ?: y_ix_void; + + z_sg = x_sg ?: y; + z_sg = x_sg ?: y_ix; + z_sg = x_sg ?: y_sg; + z_sg = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_sg ?: y_ix_void; + + z_sg = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_sg = x_ix_void ?: y; + z_sg = x_ix_void ?: y_ix; + z_sg = x_ix_void ?: y_sg; + z_sg = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_ix_void ?: y_ix_void; + + z_uix = x_ix ?: y; + z_uix = x_ix ?: y_ix; + z_uix = x_ix ?: y_sg; + z_uix = x_ix ?: y_uix; + z_uix = x_ix ?: y_ix_void; + + z_uix = x_sg ?: y; + z_uix = x_sg ?: y_ix; + z_uix = x_sg ?: y_sg; + z_uix = x_sg ?: y_uix; + z_uix = x_sg ?: y_ix_void; + + z_uix = x_uix ?: y; + z_uix = x_uix ?: y_ix; + z_uix = x_uix ?: y_sg; + z_uix = x_uix ?: y_uix; + z_uix = x_uix ?: y_ix_void; + + z_uix = x_ix_void ?: y; + z_uix = x_ix_void ?: y_ix; + z_uix = x_ix_void ?: y_sg; + z_uix = x_ix_void ?: y_uix; + z_uix = x_ix_void ?: y_ix_void; + + z_ix_void = x_ix ?: y; + z_ix_void = x_ix ?: y_ix; + z_ix_void = x_ix ?: y_sg; + z_ix_void = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_ix ?: y_ix_void; + + z_ix_void = x_sg ?: y; + z_ix_void = x_sg ?: y_ix; + z_ix_void = x_sg ?: y_sg; + z_ix_void = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_sg ?: y_ix_void; + + z_ix_void = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix_void = x_ix_void ?: y; + z_ix_void = x_ix_void ?: y_ix; + z_ix_void = x_ix_void ?: y_sg; + z_ix_void = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_ix ?: y; + + z_char = x_ix ?: y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = x_ix ?: y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_ix ?: y_ix_void; + + z_char = x_sg ?: y; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_sg ?: y_ix_void; + + z_char = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_char = x_ix_void ?: y; + z_char = x_ix_void ?: y_ix; + z_char = x_ix_void ?: y_sg; + z_char = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_ix_void ?: y_ix_void; +} + diff --git a/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c b/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c new file mode 100644 index 0000000000000..fed7629b7f4b2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Test(int sel) { + char c; + char *x = &c; + char *y = &c; + char *z = sel ? x : y; + + char * __null_terminated x_nt = __unsafe_forge_null_terminated(char *, x); + char * __null_terminated y_nt = __unsafe_forge_null_terminated(char *, y); + z = sel ? x_nt : y_nt; // expected-error{{assigning to 'char *__bidi_indexable' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@-5{{consider adding '__null_terminated' to 'z'}} + // expected-note@-2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@-3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + z = sel ? x : y_nt; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + + char * __null_terminated z_nt = sel ? x_nt : y_nt; + z_nt = sel ? x_nt : y; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + z_nt = sel ? x : y_nt; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + + char * __single y_single = &c; + char * __single x_single = &c; + z_nt = sel ? x_nt : y_single; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(0)' (aka 'char *__single') and 'char *__single'}} + z_nt = sel ? x_single : y_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single' and 'char *__single __terminated_by(0)' (aka 'char *__single')}} + + char * __terminated_by(2) x_2t = __unsafe_forge_terminated_by(char *, x, 2); + z_nt = sel ? x_2t : y_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(2)' (aka 'char *__single') and 'char *__single __terminated_by(0)' (aka 'char *__single')}} + z_nt = sel ? x_nt : x_2t; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(0)' (aka 'char *__single') and 'char *__single __terminated_by(2)' (aka 'char *__single')}} + + // Type comparison takes a different path when the pointee types are not the same, exercise this + const char * __null_terminated x_c_nt = x_nt; + z_nt = sel ? x_c_nt : y_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'const char *__single __terminated_by(0)' (aka 'const char *__single') discards qualifiers}} + z_nt = sel ? x_nt : x_c_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'const char *__single __terminated_by(0)' (aka 'const char *__single') discards qualifiers}} + + z_nt = sel ? x_c_nt : x_2t; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'const char *__single __terminated_by(0)' (aka 'const char *__single') and 'char *__single __terminated_by(2)' (aka 'char *__single')}} + z_nt = sel ? x_2t : x_c_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(2)' (aka 'char *__single') and 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + + z_nt = sel ? x_c_nt : x_single; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'const char *__single __terminated_by(0)' (aka 'const char *__single') and 'char *__single'}} + z_nt = sel ? x_single : x_c_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single' and 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + + unsigned char * __null_terminated x_u_nt = x_nt; // expected-warning{{initializing 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single') with an expression of type 'char *__single __terminated_by(0)' (aka 'char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + signed char * __null_terminated y_s_nt = y_nt; // expected-warning{{initializing 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') with an expression of type 'char *__single __terminated_by(0)' (aka 'char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + z_nt = sel ? x_u_nt : y_s_nt; // expected-error{{conditional expression evaluates values with incompatible pointee types 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single') and 'signed char *__single __terminated_by(0)' (aka 'signed char *__single'); use explicit casts to perform this conversion}} + z_nt = sel ? y_s_nt : x_u_nt; // expected-error{{conditional expression evaluates values with incompatible pointee types 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') and 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single'); use explicit casts to perform this conversion}} + z_nt = sel ? y_s_nt : (signed char * __null_terminated)x_u_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + z_nt = sel ? (signed char * __single _Nullable __null_terminated) y_s_nt : (signed char * _Nullable __null_terminated)x_u_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'signed char *__single __terminated_by(0) _Nullable' (aka 'signed char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} +} diff --git a/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c b/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c new file mode 100644 index 0000000000000..8306d0774da65 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c @@ -0,0 +1,45 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +typedef int *__counted_by(10) giptr_counted_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} +typedef int *__sized_by(1) giptr_sized_t; // expected-error{{'__sized_by' inside typedef is only allowed for function type}} +typedef int *__counted_by_or_null(10) giptr_counted_t; // expected-error{{'__counted_by_or_null' inside typedef is only allowed for function type}} +typedef int *__sized_by_or_null(1) giptr_sized_t; // expected-error{{'__sized_by_or_null' inside typedef is only allowed for function type}} +typedef int *__counted_by(10) * foo_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} + +giptr_counted_t gip_cnt; +giptr_sized_t gip_siz; + +void foo() { + giptr_counted_t gip_cnt_local; + + int n; + typedef int *__counted_by(n) liptr_counted_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} +} + +typedef int *__counted_by(16) f1(void); // ok +typedef void *__sized_by(16) f2(void); // ok +typedef int *__counted_by(len) f3(int len); // ok +typedef void *__sized_by(len) f4(int len); // ok + +typedef int *__counted_by_or_null(16) f5(void); // ok +typedef void *__sized_by_or_null(16) f6(void); // ok +typedef int *__counted_by_or_null(len) f7(int len); // ok +typedef void *__sized_by_or_null(len) f8(int len); // ok + +typedef int *__counted_by(16) (*fp1)(void); // ok +typedef void *__sized_by(16) (*fp2)(void); // ok +typedef int *__counted_by(len) (*fp3)(int len); // ok +typedef void *__sized_by(len) (*fp4)(int len); // ok + +typedef int *__counted_by_or_null(16) (*fp5)(void); // ok +typedef void *__sized_by_or_null(16) (*fp6)(void); // ok +typedef int *__counted_by_or_null(len) (*fp7)(int len); // ok +typedef void *__sized_by_or_null(len) (*fp8)(int len); // ok diff --git a/clang/test/BoundsSafety/Sema/typedef-endedby.c b/clang/test/BoundsSafety/Sema/typedef-endedby.c new file mode 100644 index 0000000000000..f5c6cfd23c5ec --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typedef-endedby.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +int *gend; +// expected-error@+1{{'__ended_by' inside typedef is only allowed for function type}} +typedef int *__ended_by(gend) endedz_t; +// expected-error@+1{{'__ended_by' inside typedef is only allowed for function type}} +typedef int *__ended_by(gend) * endedz_nested_t; + + +void foo() { + endedz_t gip_cnt_local; + + int *end; + typedef int *__ended_by(end) liptr_counted_t; // expected-error{{'__ended_by' inside typedef is only allowed for function type}} +} + +typedef int *__ended_by(pend) f(int *pend); // ok +typedef int *__ended_by(pend) (*fp)(void *pend);// ok diff --git a/clang/test/BoundsSafety/Sema/typeof-func-redecl.c b/clang/test/BoundsSafety/Sema/typeof-func-redecl.c new file mode 100644 index 0000000000000..f26a8ee014e87 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typeof-func-redecl.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +void foo(int len, int *__counted_by(len) p); + +__typeof__(foo) foo; +extern __attribute__((weak_import)) __typeof__(foo) foo; + +void bar(int len, int *__counted_by(len) p) { foo(len, p); } diff --git a/clang/test/BoundsSafety/Sema/typo-error.c b/clang/test/BoundsSafety/Sema/typo-error.c new file mode 100644 index 0000000000000..8a1e3c1690248 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typo-error.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +void bar(unsigned x, void *__sized_by(z) y, unsigned z); + +// expected-note@+1{{'filter_mode' declared here}} +void foo(unsigned filter_mode) { + void *p = 0; + // expected-error@+1{{use of undeclared identifier 'filterMode'}} + bar(filterMode, p, 0); +} diff --git a/clang/test/BoundsSafety/Sema/unreachable-noret.c b/clang/test/BoundsSafety/Sema/unreachable-noret.c new file mode 100644 index 0000000000000..cfcaed172509b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unreachable-noret.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fbounds-safety -Wunreachable-code -verify %s +// RUN: %clang_cc1 -fbounds-safety -Wunreachable-code -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +#define NO_RETURN __attribute__((noreturn)) +void NO_RETURN halt(const void * const p_fatal_error); +static void NO_RETURN handler_private(const void *__sized_by(0x78) p_stack) +{ + int foo; + halt(&foo); +} + +void NO_RETURN handler_irq(const void *__sized_by(0x78) p_stack) +{ + handler_private(p_stack); +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp b/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp new file mode 100644 index 0000000000000..52082efc7adf3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-all -fexperimental-bounds-safety-attributes -verify %s +#include +typedef unsigned size_t; +class MyClass { + size_t m; + int q[__counted_by(m)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} + + // The error is because we do not have FAM support right now. Previously, this example will crash. +}; + +namespace value_dependent_assertion_violation { + // Running attribute-only mode on the C++ code below crashes + // previously. + void g(unsigned); + + template + struct S { + void f(T p) { + g(sizeof(p)); + } + }; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-late-const.c b/clang/test/BoundsSafety/Sema/unsafe-late-const.c new file mode 100644 index 0000000000000..c3ff2d04a816e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-late-const.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +unsigned global_data_const __unsafe_late_const; +void *global_data_const2 __unsafe_late_const; + +// expected-error@+2{{'__unsafe_late_const' attribute only applies to global variables}} +// expected-error@+2{{'__unsafe_late_const' attribute only applies to global variables}} +void __unsafe_late_const +test(int param __unsafe_late_const) { + static long static_local __unsafe_late_const; + // expected-error@+1{{'__unsafe_late_const' attribute only applies to global variables}} + int local __unsafe_late_const; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c b/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c new file mode 100644 index 0000000000000..9a57b35883c1b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int main() { + int * __unsafe_indexable up; + int * __bidi_indexable bp = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)bp; + int *__indexable ap = (int *)up; // expected-error{{initializing 'int *__indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = up; // expected-error{{assigning to 'int *__indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = (int *)ap; + + void *__bidi_indexable vp = up; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-bound.c b/clang/test/BoundsSafety/Sema/unsafe-to-bound.c new file mode 100644 index 0000000000000..43dc9c9b59731 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-bound.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s + +#include + +int main() { + int * __unsafe_indexable up; + int * __bidi_indexable bp = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)bp; + int *__indexable ap = (int *)up; // expected-error{{initializing 'int *__indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = up; // expected-error{{assigning to 'int *__indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = (int *)ap; + int *auto_p = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = (int*)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = (void*__bidi_indexable)0; // no error + + void *vp = up; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + vp = (void*)0; // no error + + bp = (int*)0; + + typedef int * myintptr_t; + myintptr_t mp = (myintptr_t)0; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c b/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c new file mode 100644 index 0000000000000..4810130f101c9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-unused-value -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-unused-value -verify %s + +#include + +typedef struct { + void *vptr; + int valid; +} S; + +void foo() { + int *__unsafe_indexable ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *ptrAuto = (int *__bidi_indexable)(ptrUnsafe); + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *__indexable ptrIndex = (char *__bidi_indexable)(ptrUnsafe); + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *__bidi_indexable ptrBidiIndex = (void *__indexable)(ptrUnsafe); + int *ptrAuto2 = (void *__indexable)0; + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + int *ptrAuto3 = (void *__indexable)0xdead; + + (void *__bidi_indexable) ptrIndex; + (void *__indexable) ptrIndex; + (void *__bidi_indexable) ptrBidiIndex; + (void *__indexable) ptrBidiIndex; + (int *__bidi_indexable) ptrIndex; + (int *__indexable) ptrIndex; + (int *__bidi_indexable) ptrBidiIndex; + (int *__indexable) ptrBidiIndex; + (S *__bidi_indexable) ptrIndex; + (S *__indexable) ptrIndex; + (S *__bidi_indexable) ptrBidiIndex; + (S *__indexable) ptrBidiIndex; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + ptrAuto = (char *__bidi_indexable) (0xdead); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (char *__single) (0xdead); + (char *) (0xdead); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (char *__bidi_indexable) 0xdead; + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (S *__indexable) 0xdead; + + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (void *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (void *__indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (int *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (int *__indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (S *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (S *__indexable) ptrUnsafe; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-single.c b/clang/test/BoundsSafety/Sema/unsafe-to-single.c new file mode 100644 index 0000000000000..69555e1919ea7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-single.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-unused-value -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-unused-value -verify %s + +#include + +struct SizedByData { + void *__sized_by(len) data; + unsigned len; +}; + +void unsafe_to_sizedby(int *__unsafe_indexable ptrUnsafe) { + struct SizedByData st; + st.data = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void unsafe_to_single(int *__unsafe_indexable ptrUnsafe) { + int *__single sp = ptrUnsafe; // expected-error-re{{initializing {{.+}} with an expression of incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +struct EndedByData { + char *end; + char *__ended_by(end) begin; +}; + +void unsafe_to_endedby(struct EndedByData *st) { + int *__unsafe_indexable ptrUnsafe; + st->begin = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + st->end = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +// expected-note@+1{{passing argument to parameter 'arg' here}} +void func_with_single(int *arg); +void unsafe_to_single_parameter(void) { + int *__unsafe_indexable ptrUnsafe; + func_with_single(ptrUnsafe); // expected-error-re{{passing '{{.+}}*__unsafe_indexable' to parameter of incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} +} + +typedef int * myintptr_t; +void null_to_single(void) { + myintptr_t __single dst = (myintptr_t)0; +} + +void pointer_casts(int *__unsafe_indexable ptrUnsafe) { + int *__single ptrSingle = (char *)ptrUnsafe; // expected-error-re{{initializing {{.+}} with an expression of incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ptrSingle = (char *__unsafe_indexable)ptrSingle; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ptrSingle = (char *__unsafe_indexable)ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void unsafe_to_single_nested(void) { + int *__unsafe_indexable* ptrUnsafeNested; + int **ptr2d = ptrUnsafeNested; // expected-error{{initializing 'int *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__unsafe_indexable*__bidi_indexable'}} +} + +void pointer_cast_2d(void) { + int **ptr2d; + char **cptr2d = (char**)ptr2d; +} + +void pointer_cast_2d_unsafe(void) { + int *__unsafe_indexable*ptr2d; + // expected-error@+1{{initializing 'char *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__unsafe_indexable*__bidi_indexable'}} + char **cptr2d = (char**)ptr2d; +} + +void pointer_cast_2d_unsafe_explicit(void) { + int **ptr2d; + // expected-error@+1{{initializing 'char *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__unsafe_indexable*__bidi_indexable'}} + char **cptr2d = (char *__unsafe_indexable*)ptr2d; +} + +void pointer_cast_2d_unsafe_to_safe_explicit(void) { + int *__unsafe_indexable*ptr2d; + // expected-error@+1{{initializing 'char *__unsafe_indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__single*__bidi_indexable'}} + char *__unsafe_indexable*cptr2d = (char *__single*)ptr2d; +} diff --git a/clang/test/BoundsSafety/Sema/warn-access-vla.c b/clang/test/BoundsSafety/Sema/warn-access-vla.c new file mode 100644 index 0000000000000..906bf3490f762 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/warn-access-vla.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int f; + int vla[]; +}; + +void Test() { + struct Foo f; + (void) f.vla; + // expected-warning@-1{{accessing elements of an unannotated incomplete array always fails at runtime}} +} diff --git a/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c b/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c new file mode 100644 index 0000000000000..dae73fe23e903 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c @@ -0,0 +1,102 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void outputs(void) { + char * __bidi_indexable c_b_i; + __asm__ volatile ("xyz %0" : "=r" (c_b_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + char * __indexable c_i; + __asm__ volatile ("xyz %0" : "=r" (c_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + int n; + char * __counted_by(n) c_b; + __asm__ volatile ("xyz %0" : "=r" (c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + char * __single s; + __asm__ volatile ("xyz %0" : "=r" (s)); + + char * r; + __asm__ volatile ("xyz %0" : "=r" (r)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void output_count(void) { + int n; + char * __counted_by(n) c_b; + __asm__ volatile ("xyz %0" : "=r" (n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} +} + +struct Foo { + int n; + char * __counted_by(n) c_b; +}; + +void output_field(void) { + struct Foo f; + __asm__ volatile ("xyz %0" : "=r" (f.c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + __asm__ volatile ("xyz %0" : "=r" (f.n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} +} + +void output_ended_by( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" : "=r" (c_e_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void output_end( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" : "=r" (end)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void input_ended_by( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" :: "r" (c_e_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + __asm__ volatile ("xyz %0" :: "r" (end)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void inputs(unsigned long long temp) { + unsigned foo; + + unsigned * __bidi_indexable u_b_i; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_b_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + unsigned * __indexable u_i; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + int n; + unsigned * __counted_by(n) u_c_b; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} + + unsigned * __single u_s; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_s)); + + unsigned * u_r; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_r)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} diff --git a/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c b/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c new file mode 100644 index 0000000000000..7f9ad93bce532 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Foo(int *__counted_by(len) buf, int len) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + + +int Test() { + int arr[10]; + Foo(arr, trap_if_bigger_than_max(get_len(arr))); + unsigned long len = trap_if_bigger_than_max(get_len(arr)); + Foo(arr, len); + return 0; +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c b/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c new file mode 100644 index 0000000000000..75a4074b6e96e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wpointer-arith -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wpointer-arith -verify %s + +// expected-no-diagnostics + +#include + +struct S { + int *__sized_by(l) bp; + int l; +}; + +int main() { + struct S s; + + int arr[10]; + s.bp = arr; + s.l = 10; + + return 0; +} diff --git a/clang/test/CXX/drs/cwg158.cpp b/clang/test/CXX/drs/cwg158.cpp index 9301c790297e9..2a74438264777 100644 --- a/clang/test/CXX/drs/cwg158.cpp +++ b/clang/test/CXX/drs/cwg158.cpp @@ -1,12 +1,14 @@ -// RUN: %clang_cc1 -triple x86_64-linux -std=c++98 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-linux -std=c++11 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-linux -std=c++14 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-linux -std=c++1z %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c++98 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck --check-prefixes=CHECK %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c++11 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck --check-prefixes=CHECK %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c++14 %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck --check-prefixes=CHECK %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c++1z %s -O3 -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck --check-prefixes=CHECK %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c++1z %s -O3 -pointer-tbaa -disable-llvm-passes -pedantic-errors -emit-llvm -o - | FileCheck --check-prefixes=CHECK,POINTER-TBAA %s // cwg158: yes // CHECK-LABEL: define {{.*}} @_Z1f const int *f(const int * const *p, int **q) { + // CHECK: load ptr, ptr %p.addr // CHECK: load ptr, {{.*}}, !tbaa ![[INTPTR_TBAA:[^,]*]] const int *x = *p; // CHECK: store ptr null, {{.*}}, !tbaa ![[INTPTR_TBAA]] @@ -18,6 +20,7 @@ struct A {}; // CHECK-LABEL: define {{.*}} @_Z1g const int *(A::*const *g(const int *(A::* const **p)[3], int *(A::***q)[3]))[3] { + // CHECK: load ptr, ptr %p.addr // CHECK: load ptr, {{.*}}, !tbaa ![[MEMPTR_TBAA:[^,]*]] const int *(A::*const *x)[3] = *p; // CHECK: store ptr null, {{.*}}, !tbaa ![[MEMPTR_TBAA]] @@ -25,3 +28,16 @@ const int *(A::*const *g(const int *(A::* const **p)[3], int *(A::***q)[3]))[3] return x; } +// CHECK-LABEL: define {{.*}} @_Z1h +const int * h(const int * (*p)[10], int *(*q)[9]) { + // CHECK: load ptr, ptr %p.addr, align 8, !tbaa [[PTRARRAY_TBAA:!.+]] + const int * x = *p[0]; + + // CHECK: load ptr, ptr %q.addr, align 8, !tbaa [[PTRARRAY_TBAA]] + *q[0] = 0; + return x; +} + +// POINTER-TBAA: [[PTRARRAY_TBAA]] = !{[[PTRARRAY_TY:!.+]], [[PTRARRAY_TY]], i64 0} +// POINTER-TBAA: [[PTRARRAY_TY]] = !{!"p2 int", [[ANYPTR:!.+]], i64 0} +// POINTER-TBAA: [[ANYPTR]] = !{!"any pointer" diff --git a/clang/test/CodeGen/tbaa-pointers.c b/clang/test/CodeGen/tbaa-pointers.c index 75d8c3d501750..dfb573a4bda53 100644 --- a/clang/test/CodeGen/tbaa-pointers.c +++ b/clang/test/CodeGen/tbaa-pointers.c @@ -116,17 +116,96 @@ void p2struct(struct S1 **ptr) { // COMMON-LABEL: define void @p2struct( // COMMON-SAME: ptr noundef [[PTR:%.+]]) // COMMON: [[PTR_ADDR:%.+]] = alloca ptr, align 8 - // ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_0:!.+]] - // ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_0]] - // ENABLED-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[P1S1_:!.+]] - // DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] - // DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] - // DEFAULT-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]] + // ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_TAG:!.+]] + // DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] + // ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_TAG]] + // DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] + // ENABLED-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[P1S1_TAG:!.+]] + // DEFAULT-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]] + // COMMON-NEXT: ret void + // + *ptr = 0; +} + +void p2struct_const(struct S1 const **ptr) { + // COMMON-LABEL: define void @p2struct_const( + // COMMON-SAME: ptr noundef [[PTR:%.+]]) + // COMMON: [[PTR_ADDR:%.+]] = alloca ptr, align 8 + // COMMON-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR:!.+]] + // COMMON-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] + // ENABLED-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[P1S1_TAG]] + // DEFAULT-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]] // COMMON-NEXT: ret void // *ptr = 0; } +struct S2 { + struct S1 *s; +}; + +void p2struct2(struct S2 *ptr) { + // COMMON-LABEL: define void @p2struct2( + // COMMON-SAME: ptr noundef [[PTR:%.+]]) + // COMMON: [[PTR_ADDR:%.+]] = alloca ptr, align 8 + // ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P1S2_TAG:!.+]] + // ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P1S2_TAG]] + // ENABLED-NEXT: [[S:%.+]] = getelementptr inbounds %struct.S2, ptr [[BASE]], i32 0, i32 0 + // ENABLED-NEXT: store ptr null, ptr [[S]], align 8, !tbaa [[S2_S_TAG:!.+]] + // DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] + // DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] + // DEFAULT-NEXT: [[S:%.+]] = getelementptr inbounds %struct.S2, ptr [[BASE]], i32 0, i32 0 + // DEFAULT-NEXT: store ptr null, ptr [[S]], align 8, !tbaa [[S2_S_TAG:!.+]] + // COMMON-NEXT: ret void + ptr->s = 0; +} + + +void vla1(int n, int ptr[][n], int idx) { +// COMMON-LABEL: define void @vla1( +// COMMON-SAME: i32 noundef [[N:%.+]], ptr noundef [[PTR:%.+]], i32 noundef [[IDX:%.+]]) +// COMMON: [[N_ADDR:%.+]] = alloca i32, align 4 +// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8 +// COMMON-NEXT: [[IDX_ADDR:%.+]] = alloca i32, align 4 +// COMMON-NEXT: store i32 [[N]], ptr [[N_ADDR]], align 4, !tbaa [[INT_TY:!.+]] +// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P1INT0:!.+]] +// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] +// COMMON-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4, !tbaa [[INT_TY]] +// COMMON-NEXT: [[L:%.+]] = load i32, ptr [[N_ADDR]], align 4, !tbaa [[INT_TY]] +// COMMON-NEXT: [[L_EXT:%.+]] = zext i32 [[L]] to i64 +// ENABLED-NEXT: [[L_PTR:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P1INT0]] +// DEFAULT-NEXT: [[L_PTR:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] +// COMMON-NEXT: [[L_IDX:%.+]] = load i32, ptr [[IDX_ADDR]], align 4, !tbaa [[INT_TY]] +// COMMON-NEXT: [[IDX_EXT:%.+]] = sext i32 [[L_IDX]] to i64 +// COMMON-NEXT: [[MUL:%.+]] = mul nsw i64 [[IDX_EXT]], [[L_EXT]] +// COMMON-NEXT: [[GEP1:%.+]] = getelementptr inbounds i32, ptr [[L_PTR]], i64 [[MUL]] +// COMMON-NEXT: [[GEP2:%.+]] = getelementptr inbounds i32, ptr [[GEP1]], i64 0 +// COMMON-NEXT: store i32 0, ptr [[GEP2]], align 4, !tbaa [[INT_TAG:!.+]] +// ENABLED-NEXT: ret void + + ptr[idx][0] = 0; +} + +typedef struct { + int i1; +} TypedefS; + +void unamed_struct_typedef(TypedefS *ptr) { +// COMMON-LABEL: define void @unamed_struct_typedef( +// COMMON-SAME: ptr noundef %ptr) +// COMMON-NEXT: entry: +// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8 +// DEFAULT-NEXT: store ptr %ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] +// DEFAULT-NEXT: [[L0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]] +// ENABLED-NEXT: store ptr %ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P1TYPEDEF:!.+]] +// ENABLED-NEXT: [[L0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P1TYPEDEF]] +// COMMON-NEXT: [[GEP:%.+]] = getelementptr inbounds %struct.TypedefS, ptr [[L0]], i32 0, i32 0 +// COMMON-NEXT: store i32 0, ptr [[GEP]], align 4 +// COMMON-NEXT: ret void + + ptr->i1 = 0; +} + // ENABLED: [[P2INT_0]] = !{[[P2INT:!.+]], [[P2INT]], i64 0} // ENABLED: [[P2INT]] = !{!"p2 int", [[ANY_POINTER:!.+]], i64 0} // DEFAULT: [[ANYPTR]] = !{[[ANY_POINTER:!.+]], [[ANY_POINTER]], i64 0} @@ -145,3 +224,17 @@ void p2struct(struct S1 **ptr) { // ENABLED: [[P2CHAR]] = !{!"p2 omnipotent char", [[ANY_POINTER]], i64 0} // ENABLED: [[P1CHAR_0]] = !{[[P1CHAR:!.+]], [[P1CHAR]], i64 0} // ENABLED: [[P1CHAR]] = !{!"p1 omnipotent char", [[ANY_POINTER]], i64 0} +// ENABLED: [[P2S1_TAG]] = !{[[P2S1:!.+]], [[P2S1]], i64 0} +// ENABLED: [[P2S1]] = !{!"p2 _ZTS2S1", [[ANY_POINTER]], i64 0} +// ENABLED: [[P1S1_TAG:!.+]] = !{[[P1S1:!.+]], [[P1S1]], i64 0} +// ENABLED: [[P1S1]] = !{!"p1 _ZTS2S1", [[ANY_POINTER]], i64 0} +// ENABLED: [[P1S2_TAG]] = !{[[P1S2:!.+]], [[P1S2]], i64 0} +// ENABLED: [[P1S2]] = !{!"p1 _ZTS2S2", [[ANY_POINTER]], i64 0} + +// ENABLED: [[S2_S_TAG]] = !{[[S2_TY:!.+]], [[P1S1]], i64 0} +// ENABLED: [[S2_TY]] = !{!"S2", [[P1S1]], i64 0} +// DEFAULT: [[S2_S_TAG]] = !{[[S2_TY:!.+]], [[ANY_POINTER]], i64 0} +// DEFAULT: [[S2_TY]] = !{!"S2", [[ANY_POINTER]], i64 0} +// COMMON: [[INT_TAG]] = !{[[INT_TY:!.+]], [[INT_TY]], i64 0} +// COMMON: [[INT_TY]] = !{!"int", [[CHAR]], i64 0} +// ENABLED: [[P1TYPEDEF]] = !{[[ANY_POINTER]], [[ANY_POINTER]], i64 0} diff --git a/clang/test/CodeGen/tbaa-reference.cpp b/clang/test/CodeGen/tbaa-reference.cpp index c4d9e70a8b07f..8395badf35ded 100644 --- a/clang/test/CodeGen/tbaa-reference.cpp +++ b/clang/test/CodeGen/tbaa-reference.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-linux -O1 -disable-llvm-passes %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,OLD-PATH +// RUN: %clang_cc1 -triple x86_64-linux -O1 -disable-llvm-passes -pointer-tbaa %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,OLD-PATH-POINTER // RUN: %clang_cc1 -triple x86_64-linux -O1 -disable-llvm-passes %s -emit-llvm -new-struct-path-tbaa -o - | FileCheck %s -check-prefixes=CHECK,NEW-PATH +// RUN: %clang_cc1 -triple x86_64-linux -O1 -disable-llvm-passes %s -pointer-tbaa -emit-llvm -new-struct-path-tbaa -o - | FileCheck %s -check-prefixes=CHECK,NEW-PATH-POINTER // // Check that we generate correct TBAA information for reference accesses. @@ -14,13 +16,13 @@ struct B { B::B(S &s) : s(s) { // CHECK-LABEL: _ZN1BC2ER1S // Check initialization of the reference parameter. -// CHECK: store ptr {{.*}}, ptr {{.*}}, !tbaa [[TAG_pointer:!.*]] +// CHECK: store ptr {{.*}}, ptr %s.addr, align 8, !tbaa [[TAG_S_PTR:!.*]] // Check loading of the reference parameter. -// CHECK: load ptr, ptr {{.*}}, !tbaa [[TAG_pointer]] +// CHECK: load ptr, ptr {{.*}}, !tbaa [[TAG_S_PTR:!.*]] // Check initialization of the reference member. -// CHECK: store ptr {{.*}}, ptr {{.*}}, !tbaa [[TAG_pointer]] +// CHECK: store ptr {{.*}}, ptr {{.*}}, !tbaa [[TAG_S_PTR]] } S &B::get() { @@ -30,16 +32,32 @@ S &B::get() { return s; } -// OLD-PATH-DAG: [[TAG_pointer]] = !{[[TYPE_pointer:!.*]], [[TYPE_pointer]], i64 0} +// OLD-PATH-DAG: [[TAG_S_PTR]] = !{[[TYPE_pointer:!.*]], [[TYPE_pointer]], i64 0} // OLD-PATH-DAG: [[TAG_B_s]] = !{[[TYPE_B:!.*]], [[TYPE_pointer]], i64 0} // // OLD-PATH-DAG: [[TYPE_B]] = !{!"_ZTS1B", [[TYPE_pointer]], i64 0} // OLD-PATH-DAG: [[TYPE_pointer]] = !{!"any pointer", [[TYPE_char:!.*]], i64 0} // OLD-PATH-DAG: [[TYPE_char]] = !{!"omnipotent char", {{!.*}}, i64 0} -// NEW-PATH-DAG: [[TAG_pointer]] = !{[[TYPE_pointer:!.*]], [[TYPE_pointer]], i64 0, i64 8} +// OLD-PATH-POINTER-DAG: [[TAG_S_PTR]] = !{[[TYPE_S_PTR:!.*]], [[TYPE_S_PTR]], i64 0} +// OLD-PATH-POINTER-DAG: [[TAG_B_s]] = !{[[TYPE_B:!.*]], [[TYPE_S_PTR:!.*]], i64 0} +// +// OLD-PATH-POINTER-DAG: [[TYPE_B]] = !{!"_ZTS1B", [[TYPE_S_PTR:!.*]], i64 0} +// OLD-PATH-POINTER-DAG: [[TYPE_pointer:!.*]] = !{!"any pointer", [[TYPE_char:!.*]], i64 0} +// OLD-PATH-POINTER-DAG: [[TYPE_char]] = !{!"omnipotent char", {{!.*}}, i64 0} +// OLD-PATH-POINTER-DAG: [[TYPE_S_PTR]] = !{!"p1 _ZTS1S", [[TYPE_pointer]], i64 0} + +// NEW-PATH-DAG: [[TAG_S_PTR]] = !{[[TYPE_pointer:!.*]], [[TYPE_pointer]], i64 0, i64 8} // NEW-PATH-DAG: [[TAG_B_s]] = !{[[TYPE_B:!.*]], [[TYPE_pointer]], i64 0, i64 8} // // NEW-PATH-DAG: [[TYPE_B]] = !{[[TYPE_char:!.*]], i64 8, !"_ZTS1B", [[TYPE_pointer]], i64 0, i64 8} // NEW-PATH-DAG: [[TYPE_pointer]] = !{[[TYPE_char:!.*]], i64 8, !"any pointer"} // NEW-PATH-DAG: [[TYPE_char]] = !{{{!.*}}, i64 1, !"omnipotent char"} + +// NEW-PATH-POINTER-DAG: [[TAG_S_PTR]] = !{[[TYPE_S_PTR:!.*]], [[TYPE_S_PTR]], i64 0, i64 8} +// NEW-PATH-POINTER-DAG: [[TAG_B_s]] = !{[[TYPE_B:!.*]], [[TYPE_S_PTR]], i64 0, i64 8} +// +// NEW-PATH-POINTER-DAG: [[TYPE_B]] = !{[[TYPE_char:!.*]], i64 8, !"_ZTS1B", [[TYPE_S_PTR]], i64 0, i64 8} +// NEW-PATH-POINTER-DAG: [[TYPE_S_PTR]] = !{[[TYPE_pointer:!.+]], i64 8, !"p1 _ZTS1S"} +// NEW-PATH-POINTER-DAG: [[TYPE_pointer]] = !{[[TYPE_char:!.*]], i64 8, !"any pointer"} +// NEW-PATH-POINTER-DAG: [[TYPE_char]] = !{{{!.*}}, i64 1, !"omnipotent char"} diff --git a/clang/test/CodeGenCXX/noescape.cpp b/clang/test/CodeGenCXX/noescape.cpp index c4968ffec1aa1..fcc7258c869ad 100644 --- a/clang/test/CodeGenCXX/noescape.cpp +++ b/clang/test/CodeGenCXX/noescape.cpp @@ -6,6 +6,7 @@ struct S { S &operator=(int * __attribute__((noescape))); void m0(int *, int * __attribute__((noescape))); virtual void vm1(int *, int * __attribute__((noescape))); + virtual void vm2(int *, S __attribute__((noescape))); }; // CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} nocapture noundef {{%.*}}) @@ -23,6 +24,9 @@ void S::m0(int *, int * __attribute__((noescape))) {} // CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} nocapture noundef {{%.*}}) void S::vm1(int *, int * __attribute__((noescape))) {} +// CHECK-NOT: nocapture +void S::vm2(int *, S __attribute__((noescape))) {} + // CHECK-LABEL: define{{.*}} void @_Z5test0P1SPiS1_( // CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} nocapture noundef {{.*}}) // CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} nocapture noundef {{.*}}) diff --git a/clang/test/CodeGenObjC/noescape.m b/clang/test/CodeGenObjC/noescape.m index 3dfcff1cdf5bf..41b9d5cc0635a 100644 --- a/clang/test/CodeGenObjC/noescape.m +++ b/clang/test/CodeGenObjC/noescape.m @@ -8,11 +8,16 @@ long long *ll; } __attribute__((transparent_union)); +struct S { + int *p; +}; + void escapingFunc0(BlockTy); void noescapeFunc0(id, __attribute__((noescape)) BlockTy); void noescapeFunc1(__attribute__((noescape)) int *); void noescapeFunc2(__attribute__((noescape)) id); void noescapeFunc3(__attribute__((noescape)) union U); +void noescapeFunc4(__attribute__((noescape)) struct S s); // Block descriptors of non-escaping blocks don't need pointers to copy/dispose // helper functions. @@ -53,6 +58,11 @@ void test3(union U u) { noescapeFunc3(u); } +// CHECK-NOT: nocapture +void testNonPtr(struct S s) { + noescapeFunc4(s); +} + // CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) // CHECK-LABEL: define{{.*}} void @test4( diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/embedded/usr/include/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/embedded/usr/include/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/embedded/usr/local/include/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/embedded/usr/local/include/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/usr/include/c++/v1/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/usr/include/c++/v1/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/usr/local/include/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/usr/local/include/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/darwin-embedded-search-paths-libcxx.c b/clang/test/Driver/darwin-embedded-search-paths-libcxx.c new file mode 100644 index 0000000000000..0f9a8467b061a --- /dev/null +++ b/clang/test/Driver/darwin-embedded-search-paths-libcxx.c @@ -0,0 +1,45 @@ +// REQUIRES: default-cxx-stdlib=libc++ +// UNSUPPORTED: system-windows +// Windows is unsupported because we use the Unix path separator `/` in the test. + +// Unlike the Darwin driver, the MachO driver doesn't add any framework search paths, +// only the normal header ones. +// RUN: %clang -x c -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,NO-CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// Unlike the Darwin driver, the MachO driver doesn't default to libc++, but when +// CLANG_DEFAULT_CXX_STDLIB is libc++ then the MachO driver should find the search path. +// RUN: %clang -x c++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// If the user requests libc++, the MachO driver should still find the search path. +// RUN: %clang -x c++ -stdlib=libc++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// Verify that embedded uses can swap in alternate usr/include and usr/local/include directories. +// usr/local/include is specified in the driver as -internal-isystem, however, the driver generated +// paths come before the paths in the driver arguments. In order to keep usr/local/include in the +// same position, -isystem has to be used instead of -Xclang -internal-isystem. There isn't an +// -externc-isystem, but it's ok to use -Xclang -internal-externc-isystem since the driver doesn't +// use that if -nostdlibinc or -nostdinc is passed. +// RUN: %clang -x c++ -stdlib=libc++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk \ +// RUN: -nostdlibinc -isystem %S/Inputs/MacOSX15.1.sdk/embedded/usr/local/include \ +// RUN: -Xclang -internal-externc-isystem -Xclang %S/Inputs/MacOSX15.1.sdk/embedded/usr/include \ +// RUN: -### -c %s 2>&1 | FileCheck --check-prefixes=CC1,NO-CXX,EULI,CI,EUI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + + +// The ordering of these flags doesn't matter, and so this test is a little +// fragile. i.e. all of the -internal-isystem paths will be searched before the +// -internal-externc-isystem ones, and their order on the command line doesn't +// matter. The line order here is just the current order that the driver writes +// the cc1 arguments. + +// CC1: "-cc1" +// NO-CXX-NOT: "-internal-isystem" "{{.*}}/include/c++/v1" +// CXX-SAME: "-internal-isystem" "{{.*}}/include/c++/v1" +// ULI-SAME: "-internal-isystem" "[[SDKROOT]]/usr/local/include" +// EULI-SAME: "-isystem" "[[SDKROOT]]/embedded/usr/local/include" +// CI-SAME: "-internal-isystem" "{{.*}}/clang/{{[[:digit:].]*}}/include" +// UI-SAME: "-internal-externc-isystem" "[[SDKROOT]]/usr/include" +// EUI-SAME: "-internal-externc-isystem" "[[SDKROOT]]/embedded/usr/include" +// NO-FW-NOT: "-internal-iframework" diff --git a/clang/test/Driver/darwin-embedded-search-paths.c b/clang/test/Driver/darwin-embedded-search-paths.c new file mode 100644 index 0000000000000..0d39c15e0c015 --- /dev/null +++ b/clang/test/Driver/darwin-embedded-search-paths.c @@ -0,0 +1,45 @@ +// REQUIRES: !(default-cxx-stdlib=libc++) +// UNSUPPORTED: system-windows +// Windows is unsupported because we use the Unix path separator `/` in the test. + +// Unlike the Darwin driver, the MachO driver doesn't add any framework search paths, +// only the normal header ones. +// RUN: %clang -x c -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,NO-CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// Unlike the Darwin driver, the MachO driver doesn't default to libc++, and unless +// CLANG_DEFAULT_CXX_STDLIB is libc++ it won't add any search paths. +// RUN: %clang -x c++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,NO-CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// However, if the user requests libc++, the MachO driver should find the search path. +// RUN: %clang -x c++ -stdlib=libc++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CC1,CXX,ULI,CI,UI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + +// Verify that embedded uses can swap in alternate usr/include and usr/local/include directories. +// usr/local/include is specified in the driver as -internal-isystem, however, the driver generated +// paths come before the paths in the driver arguments. In order to keep usr/local/include in the +// same position, -isystem has to be used instead of -Xclang -internal-isystem. There isn't an +// -externc-isystem, but it's ok to use -Xclang -internal-externc-isystem since the driver doesn't +// use that if -nostdlibinc or -nostdinc is passed. +// RUN: %clang -x c++ -stdlib=libc++ -target arm64-apple-none-macho -isysroot %S/Inputs/MacOSX15.1.sdk \ +// RUN: -nostdlibinc -isystem %S/Inputs/MacOSX15.1.sdk/embedded/usr/local/include \ +// RUN: -Xclang -internal-externc-isystem -Xclang %S/Inputs/MacOSX15.1.sdk/embedded/usr/include \ +// RUN: -### -c %s 2>&1 | FileCheck --check-prefixes=CC1,NO-CXX,EULI,CI,EUI,NO-FW -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s + + +// The ordering of these flags doesn't matter, and so this test is a little +// fragile. i.e. all of the -internal-isystem paths will be searched before the +// -internal-externc-isystem ones, and their order on the command line doesn't +// matter. The line order here is just the current order that the driver writes +// the cc1 arguments. + +// CC1: "-cc1" +// NO-CXX-NOT: "-internal-isystem" "{{.*}}/include/c++/v1" +// CXX-SAME: "-internal-isystem" "{{.*}}/include/c++/v1" +// ULI-SAME: "-internal-isystem" "[[SDKROOT]]/usr/local/include" +// EULI-SAME: "-isystem" "[[SDKROOT]]/embedded/usr/local/include" +// CI-SAME: "-internal-isystem" "{{.*}}/clang/{{[[:digit:].]*}}/include" +// UI-SAME: "-internal-externc-isystem" "[[SDKROOT]]/usr/include" +// EUI-SAME: "-internal-externc-isystem" "[[SDKROOT]]/embedded/usr/include" +// NO-FW-NOT: "-internal-iframework" diff --git a/clang/test/ExtractAPI/objc_external_category.m b/clang/test/ExtractAPI/objc_external_category.m index 8afc92489f28b..1947237d088e8 100644 --- a/clang/test/ExtractAPI/objc_external_category.m +++ b/clang/test/ExtractAPI/objc_external_category.m @@ -46,7 +46,7 @@ @interface ExtInterface // Symbol graph from the build without extension SGFs should be identical to main symbol graph with extension SGFs // RUN: diff %t/symbols/Module.symbols.json %t/ModuleNoExt.symbols.json -// RUN: FileCheck %s --input-file %t/symbols/ExternalModule@Module.symbols.json --check-prefix EXT +// RUN: FileCheck %s --input-file %t/symbols/Module@ExternalModule.symbols.json --check-prefix EXT // EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(py)Property $ c:objc(cs)ExtInterface" // EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(im)InstanceMethod $ c:objc(cs)ExtInterface" // EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(cm)ClassMethod $ c:objc(cs)ExtInterface" @@ -55,3 +55,10 @@ @interface ExtInterface // EXT-DAG: "!testLabel": "c:objc(cs)ExtInterface(cm)ClassMethod" // EXT-NOT: "!testLabel": "c:objc(cs)ExtInterface" // EXT-NOT: "!testLabel": "c:objc(cs)ModInterface" + +// Ensure that the 'module' metadata for the extension symbol graph should still reference the +// declaring module + +// RUN: FileCheck %s --input-file %t/symbols/Module@ExternalModule.symbols.json --check-prefix META +// META: "module": { +// META-NEXT: "name": "Module", diff --git a/clang/test/Frontend/Inputs/macro_defined_type.h b/clang/test/Frontend/Inputs/macro_defined_type.h new file mode 100644 index 0000000000000..fa90b5e9e6a1c --- /dev/null +++ b/clang/test/Frontend/Inputs/macro_defined_type.h @@ -0,0 +1,4 @@ +#pragma clang system_header + +#define _SYS_NODEREF __attribute__((noderef)) +#define _SYS_LIBCPP_FLOAT_ABI __attribute__((pcs("aapcs"))) diff --git a/clang/test/Frontend/macro_defined_type.cpp b/clang/test/Frontend/macro_defined_type.cpp index 71a0ff18477d8..e796d9b37132c 100644 --- a/clang/test/Frontend/macro_defined_type.cpp +++ b/clang/test/Frontend/macro_defined_type.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-linux-gnu %s +// RUN: %clang_cc1 -I%S/Inputs -fsyntax-only -verify -triple x86_64-linux-gnu %s + +#include #define NODEREF __attribute__((noderef)) @@ -14,12 +16,26 @@ void Func() { auto NODEREF auto_i2 = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} } +// The diagnostic message is hard-coded as 'noderef' so using a system macro doesn't change the behavior +void Func_system_macro() { + int _SYS_NODEREF i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int _SYS_NODEREF *i_ptr; + + auto _SYS_NODEREF *auto_i_ptr2 = i_ptr; + auto _SYS_NODEREF auto_i2 = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + + // Added test for fix for P41835 #define _LIBCPP_FLOAT_ABI __attribute__((pcs("aapcs"))) struct A { _LIBCPP_FLOAT_ABI int operator()() throw(); // expected-warning{{'pcs' calling convention is not supported for this target}} }; +struct A_system_macro { + _SYS_LIBCPP_FLOAT_ABI int operator()() throw(); // expected-warning{{'_SYS_LIBCPP_FLOAT_ABI' calling convention is not supported for this target}} +}; + // Added test for fix for PR43315 #define a __attribute__((__cdecl__, __regparm__(0))) int(a b)(); diff --git a/clang/test/Lexer/gnu-flags.c b/clang/test/Lexer/gnu-flags.c index 4d6d216b101f4..efd0d3e85d249 100644 --- a/clang/test/Lexer/gnu-flags.c +++ b/clang/test/Lexer/gnu-flags.c @@ -17,6 +17,8 @@ #if ALL || ZEROARGS +// expected-warning@+9 {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} +// expected-note@+4 {{macro 'efoo' defined here}} // expected-warning@+3 {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} #endif diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index a218f3fc07115..baad70cafcb43 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -204,6 +204,7 @@ // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local) // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function, SubjectMatchRule_field) +// CHECK-NEXT: UnsafeLateConst (SubjectMatchRule_variable_is_global) // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: VTablePointerAuthentication (SubjectMatchRule_record) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) diff --git a/clang/test/Preprocessor/darwin-predefines.c b/clang/test/Preprocessor/darwin-predefines.c new file mode 100644 index 0000000000000..c76eba6db1b6b --- /dev/null +++ b/clang/test/Preprocessor/darwin-predefines.c @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -E -dM -triple arm64-apple-macosx15.0.0 -target-cpu apple-m1 %s | FileCheck %s -check-prefix CHECK-MACOSX + +// CHECK-MACOSX: #define __APPLE_CC__ +// CHECK-MACOSX: #define __APPLE__ +// CHECK-MACOSX: #define __ARM_64BIT_STATE 1 +// CHECK-MACOSX-NOT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ +// CHECK-MACOSX-NOT: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +// CHECK-MACOSX: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ 150000 +// CHECK-MACOSX: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 150000 +// CHECK-MACOSX-NOT: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ +// CHECK-MACOSX-NOT: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ +// CHECK-MACOSX: #define __MACH__ +// CHECK-MACOSX: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple arm64-apple-ios18.0.0 -target-cpu apple-a7 %s | FileCheck %s -check-prefix CHECK-IOS + +// CHECK-IOS: #define __APPLE_CC__ +// CHECK-IOS: #define __APPLE__ +// CHECK-IOS: #define __ARM_64BIT_STATE 1 +// CHECK-IOS-NOT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ +// CHECK-IOS: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 180000 +// CHECK-IOS-NOT: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +// CHECK-IOS: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 180000 +// CHECK-IOS-NOT: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ +// CHECK-IOS-NOT: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ +// CHECK-IOS: #define __MACH__ +// CHECK-IOS: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple arm64-apple-watchos11.0.0 -target-cpu apple-s4 %s | FileCheck %s -check-prefix CHECK-WATCHOS + +// CHECK-WATCHOS: #define __APPLE_CC__ +// CHECK-WATCHOS: #define __APPLE__ +// CHECK-WATCHOS: #define __ARM_64BIT_STATE 1 +// CHECK-WATCHOS-NOT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ +// CHECK-WATCHOS-NOT: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +// CHECK-WATCHOS-NOT: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +// CHECK-WATCHOS: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 110000 +// CHECK-WATCHOS-NOT: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ +// CHECK-WATCHOS: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ 110000 +// CHECK-WATCHOS: #define __MACH__ +// CHECK-WATCHOS: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple arm64-apple-tvos18.0.0 -target-cpu apple-a7 %s | FileCheck %s -check-prefix CHECK-TVOS + +// CHECK-TVOS: #define __APPLE_CC__ +// CHECK-TVOS: #define __APPLE__ +// CHECK-TVOS: #define __ARM_64BIT_STATE 1 +// CHECK-TVOS-NOT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ +// CHECK-TVOS-NOT: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +// CHECK-TVOS-NOT: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +// CHECK-TVOS: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 180000 +// CHECK-TVOS: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ 180000 +// CHECK-TVOS-NOT: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ +// CHECK-TVOS: #define __MACH__ +// CHECK-TVOS: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple arm64-apple-driverkit24.0.0 -target-cpu apple-a7 %s | FileCheck %s -check-prefix CHECK-DRIVERKIT + +// CHECK-DRIVERKIT: #define __APPLE_CC__ +// CHECK-DRIVERKIT: #define __APPLE__ +// CHECK-DRIVERKIT: #define __ARM_64BIT_STATE 1 +// CHECK-DRIVERKIT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ 240000 +// CHECK-DRIVERKIT-NOT: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +// CHECK-DRIVERKIT-NOT: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +// CHECK-DRIVERKIT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 240000 +// CHECK-DRIVERKIT-NOT: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ +// CHECK-DRIVERKIT-NOT: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ +// CHECK-DRIVERKIT: #define __MACH__ +// CHECK-DRIVERKIT: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple arm64-apple-xros2.0.0 -target-cpu apple-a12 %s | FileCheck %s -check-prefix CHECK-XROS + +// CHECK-XROS: #define __APPLE_CC__ +// CHECK-XROS: #define __APPLE__ +// CHECK-XROS: #define __ARM_64BIT_STATE 1 +// CHECK-XROS-NOT: #define __ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__ +// CHECK-XROS-NOT: #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ +// CHECK-XROS-NOT: #define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +// CHECK-XROS: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ 20000 +// CHECK-XROS-NOT: #define __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ +// CHECK-XROS-NOT: #define __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ +// CHECK-XROS: #define __MACH__ +// CHECK-XROS: #define __STDC_NO_THREADS__ diff --git a/clang/test/Preprocessor/macho-embedded-predefines.c b/clang/test/Preprocessor/macho-embedded-predefines.c index 74f29199218c4..80a85eaa973e9 100644 --- a/clang/test/Preprocessor/macho-embedded-predefines.c +++ b/clang/test/Preprocessor/macho-embedded-predefines.c @@ -1,20 +1,43 @@ +// RUN: %clang_cc1 -E -dM -triple arm64-apple-none-macho -target-cpu generic %s | FileCheck %s -check-prefix CHECK-64 + +// CHECK-64: #define __APPLE_CC__ +// CHECK-64: #define __APPLE__ +// CHECK-64: #define __ARM_64BIT_STATE 1 +// CHECK-64-NOT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ +// CHECK-64: #define __MACH__ +// CHECK-64-NOT: #define __STDC_NO_THREADS__ + // RUN: %clang_cc1 -E -dM -triple thumbv7m-apple-unknown-macho -target-cpu cortex-m3 %s | FileCheck %s -check-prefix CHECK-7M // CHECK-7M: #define __APPLE_CC__ // CHECK-7M: #define __APPLE__ // CHECK-7M: #define __ARM_ARCH_7M__ -// CHECK-7M-NOT: #define __MACH__ +// CHECK-7M-NOT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ +// CHECK-7M: #define __MACH__ +// CHECK-7M: #define __STDC_NO_THREADS__ // RUN: %clang_cc1 -E -dM -triple thumbv7em-apple-unknown-macho -target-cpu cortex-m4 %s | FileCheck %s -check-prefix CHECK-7EM // CHECK-7EM: #define __APPLE_CC__ // CHECK-7EM: #define __APPLE__ // CHECK-7EM: #define __ARM_ARCH_7EM__ -// CHECK-7EM-NOT: #define __MACH__ +// CHECK-7EM-NOT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ +// CHECK-7EM: #define __MACH__ +// CHECK-7EM: #define __STDC_NO_THREADS__ // RUN: %clang_cc1 -E -dM -triple thumbv6m-apple-unknown-macho -target-cpu cortex-m0 %s | FileCheck %s -check-prefix CHECK-6M // CHECK-6M: #define __APPLE_CC__ // CHECK-6M: #define __APPLE__ // CHECK-6M: #define __ARM_ARCH_6M__ -// CHECK-6M-NOT: #define __MACH__ +// CHECK-6M-NOT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ +// CHECK-6M: #define __MACH__ +// CHECK-6M: #define __STDC_NO_THREADS__ + +// RUN: %clang_cc1 -E -dM -triple x86_64-pc-windows-macho -target-cpu x86-64 %s | FileCheck %s -check-prefix CHECK-WINDOWS + +// CHECK-WINDOWS: #define __APPLE_CC__ +// CHECK-WINDOWS: #define __APPLE__ +// CHECK-WINDOWS-NOT: #define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ +// CHECK-WINDOWS-NOT: #define __MACH__ +// CHECK-WINDOWS: #define __STDC_NO_THREADS__ diff --git a/clang/test/Preprocessor/macro_fn.c b/clang/test/Preprocessor/macro_fn.c index 81d8363214078..2e72bd272084e 100644 --- a/clang/test/Preprocessor/macro_fn.c +++ b/clang/test/Preprocessor/macro_fn.c @@ -1,11 +1,17 @@ /* RUN: %clang_cc1 %s -Eonly -std=c89 -pedantic -verify */ +// RUN: %clang_cc1 %s -Eonly -std=c89 -pedantic -Wno-gnu-zero-variadic-macro-arguments -verify -DOMIT_VARIADIC_MACRO_ARGS -DVARIADIC_MACRO_ARGS_REMOVE_COMMA +// RUN: %clang_cc1 %s -Eonly -std=c89 -pedantic -Wno-variadic-macro-arguments-omitted -verify -DOMIT_VARIADIC_MACRO_ARGS /* PR3937 */ #define zero() 0 /* expected-note 2 {{defined here}} */ #define one(x) 0 /* expected-note 2 {{defined here}} */ #define two(x, y) 0 /* expected-note 4 {{defined here}} */ #define zero_dot(...) 0 /* expected-warning {{variadic macros are a C99 feature}} */ -#define one_dot(x, ...) 0 /* expected-warning {{variadic macros are a C99 feature}} expected-note 2{{macro 'one_dot' defined here}} */ +#define one_dot(x, ...) 0 /* expected-warning {{variadic macros are a C99 feature}} */ + +#ifndef OMIT_VARIADIC_MACRO_ARGS +/* expected-note@-3 2{{macro 'one_dot' defined here}} */ +#endif zero() zero(1); /* expected-error {{too many arguments provided to function-like macro invocation}} */ @@ -37,16 +43,24 @@ e(x) e() zero_dot() -one_dot(x) /* empty ... argument: expected-warning {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} */ -one_dot() /* empty first argument, elided ...: expected-warning {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} */ +one_dot(x) /* empty ... argument */ +one_dot() /* empty first argument, elided ... */ +#ifndef OMIT_VARIADIC_MACRO_ARGS +/* expected-warning@-4 {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} */ +/* expected-warning@-4 {{passing no argument for the '...' parameter of a variadic macro is a C23 extension}} */ +#endif /* Crash with function-like macro test at end of directive. */ #define E() (i == 0) #if E #endif - #define NSAssert(condition, desc, ...) /* expected-warning {{variadic macros are a C99 feature}} */ \ - SomeComplicatedStuff((desc), ##__VA_ARGS__) /* expected-warning {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} */ + SomeComplicatedStuff((desc), ##__VA_ARGS__) + +#ifndef VARIADIC_MACRO_ARGS_REMOVE_COMMA +/* expected-warning@-3 {{token pasting of ',' and __VA_ARGS__ is a GNU extension}} */ +#endif + NSAssert(somecond, somedesc) diff --git a/clang/test/Sema/attr-counted-by-bounds-safety-vlas.c b/clang/test/Sema/attr-counted-by-bounds-safety-vlas.c index 7d9c9a90880ff..6ed1b9512fb38 100644 --- a/clang/test/Sema/attr-counted-by-bounds-safety-vlas.c +++ b/clang/test/Sema/attr-counted-by-bounds-safety-vlas.c @@ -1,3 +1,8 @@ +// XFAIL: * +// The test fails because the full parsing logic isn't upstreamed yet, and +// in downstream we have separate functions to handle the counted_by attribute in parsing with +// and without -f(experimental-)bounds-safety enabled. rdar://126708352 + // RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety -verify %s // // This is a portion of the `attr-counted-by-vla.c` test but is checked diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c index 9ff3b080f6576..d0d4ef5751616 100644 --- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c @@ -171,13 +171,14 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { int count; }; +/* TO_UPSTREAM(BoundsSafety) ON */ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos { - // TODO: This should fail because the attribute is - // on a pointer with the pointee being a function type. - // expected-error@+1{{use of undeclared identifier 'count'}} + // disabled-expected-error@+1{{use of undeclared identifier 'count'}} + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} fn_ptr_ty __counted_by(count) fn_ptr; int count; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ struct on_member_pointer_fn_ptr_ty_ty_pos_inner { // TODO: This should fail because the attribute is diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c index 95f517e3144f7..d81254631b628 100644 --- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -172,13 +172,14 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { int count; }; +/* TO_UPSTREAM(BoundsSafety) ON */ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos { - // TODO: This should fail because the attribute is - // on a pointer with the pointee being a function type. - // expected-error@+1{{use of undeclared identifier 'count'}} + // expected-error@+2{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + // disabled-expected-error@+1{{use of undeclared identifier 'count'}} fn_ptr_ty __counted_by_or_null(count) fn_ptr; int count; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ struct on_member_pointer_fn_ptr_ty_ty_pos_inner { // TODO: This should fail because the attribute is diff --git a/clang/test/Sema/attr-noescape.c b/clang/test/Sema/attr-noescape.c index d342654281934..1748e403f12b0 100644 --- a/clang/test/Sema/attr-noescape.c +++ b/clang/test/Sema/attr-noescape.c @@ -8,5 +8,5 @@ int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribu void foo(__attribute__((noescape)) int *int_ptr, __attribute__((noescape)) int (^block)(int), - __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to pointer arguments}} + __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} } diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp b/clang/test/Sema/attr-nonblocking-constraints.cpp index cc9108c0a4fbd..bbc909f627f4c 100644 --- a/clang/test/Sema/attr-nonblocking-constraints.cpp +++ b/clang/test/Sema/attr-nonblocking-constraints.cpp @@ -144,6 +144,41 @@ void nb9() [[clang::nonblocking]] expected-note {{in template expansion here}} } +// Make sure we verify lambdas produced from template expansions. +struct HasTemplatedLambda { + void (*fptr)() [[clang::nonblocking]]; + + template + HasTemplatedLambda(const C&) + : fptr{ []() [[clang::nonblocking]] { + auto* y = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} + } } + {} +}; + +void nb9a() +{ + HasTemplatedLambda bad(42); +} + +// Templated function and lambda. +template +void TemplatedFunc(T x) [[clang::nonblocking]] { + auto* ptr = new T; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} +} + +void nb9b() [[clang::nonblocking]] { + TemplatedFunc(42); // expected-note {{in template expansion here}} + + auto foo = [](auto x) [[clang::nonblocking]] { + auto* ptr = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} + return x; + }; + + // Note that foo() won't be validated unless instantiated. + foo(42); +} + void nb10( void (*fp1)(), // expected-note {{function pointer cannot be inferred 'nonblocking'}} void (*fp2)() [[clang::nonblocking]] diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c index 07f8801787d66..5e9f17404668e 100644 --- a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c @@ -168,12 +168,14 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { int size; }; +/* TO_UPSTREAM(BoundsSafety) ON */ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos { - // TODO: This should be allowed with sized_by. - // expected-error@+1{{use of undeclared identifier 'size'}} + // disabled-expected-error@+1{{use of undeclared identifier 'size'}} + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} fn_ptr_ty __sized_by(size) fn_ptr; int size; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ struct on_member_pointer_fn_ptr_ty_ty_pos_inner { // TODO: This should be allowed with sized_by. diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c index afe5f0af28083..31051df09fda4 100644 --- a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c @@ -168,12 +168,14 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { int size; }; +/* TO_UPSTREAM(BoundsSafety) ON */ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos { - // TODO: This should be allowed with sized_by_or_null. - // expected-error@+1{{use of undeclared identifier 'size'}} + // disabled-expected-error@+2{{use of undeclared identifier 'size'}} + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} fn_ptr_ty __sized_by_or_null(size) fn_ptr; int size; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ struct on_member_pointer_fn_ptr_ty_ty_pos_inner { // TODO: This should be allowed with sized_by_or_null. diff --git a/clang/test/SemaCXX/Inputs/warn-thread-safety-parsing.h b/clang/test/SemaCXX/Inputs/warn-thread-safety-parsing.h new file mode 100644 index 0000000000000..1c55e9f668478 --- /dev/null +++ b/clang/test/SemaCXX/Inputs/warn-thread-safety-parsing.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +#define _SYS_GUARDED_BY(x) __attribute__ ((guarded_by(x))) \ No newline at end of file diff --git a/clang/test/SemaCXX/error-unsafe-buffer-usage-count-attributed-pointer-argument.cpp b/clang/test/SemaCXX/error-unsafe-buffer-usage-count-attributed-pointer-argument.cpp new file mode 100644 index 0000000000000..e4fd5f53d2ffc --- /dev/null +++ b/clang/test/SemaCXX/error-unsafe-buffer-usage-count-attributed-pointer-argument.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -Wno-all -fexperimental-bounds-safety-attributes -verify %s + +#include +#include + +namespace output_param_test { + void cb_out_ptr(int * __counted_by(n) * p, size_t n); + void cb_out_count(int * __counted_by(*n) p, size_t * n); + void cb_out_both(int * __counted_by(*n) * p, size_t * n); + + void test_no_attr(int *p, size_t n, int **q, size_t *m) { + cb_out_ptr(&p, n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(q, *m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_count(p, &n); // not output ptr no error but will be warned by -Wunsafe-buffer-usage + cb_out_count(*q, m); // not output ptr no error but will be warned by -Wunsafe-buffer-usage + + size_t local_n = n; + int * local_p = p; + + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(&local_p, local_n); + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(&local_p, &local_n); + } + + void test(int * __counted_by(n) p, size_t n, int * __counted_by(*m) *q, size_t *m) { + cb_out_ptr(&p, *m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(q, n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_both(&p, m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(q, &n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + + size_t local_n = n; + int * __counted_by(local_n) local_p = p; + + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(&local_p, n); + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(&local_p, &n); + } + + class TestClassMemberFunctions { + void test_no_attr(int *p, size_t n, int **q, size_t *m) { + cb_out_ptr(&p, n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(q, *m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_count(p, &n); // not output ptr no error but will be warned by -Wunsafe-buffer-usage + cb_out_count(*q, m); // not output ptr no error but will be warned by -Wunsafe-buffer-usage + + size_t local_n = n; + int * local_p = p; + + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(&local_p, local_n); + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(&local_p, &local_n); + } + + void test(int * __counted_by(n) p, size_t n, int * __counted_by(*m) *q, size_t *m) { + cb_out_ptr(&p, *m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(q, n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_both(&p, m); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(q, &n); // expected-error{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + + size_t local_n = n; + int * __counted_by(local_n) local_p = p; + + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + cb_out_ptr(&local_p, n); + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(*n)*' (aka 'int **')}} + cb_out_both(&local_p, &n); + } + }; +} // namespace output_param_test diff --git a/clang/test/SemaCXX/noescape-attr.cpp b/clang/test/SemaCXX/noescape-attr.cpp new file mode 100644 index 0000000000000..fd54e09d1118b --- /dev/null +++ b/clang/test/SemaCXX/noescape-attr.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +void test1(T __attribute__((noescape)) arr, int size); + +void test2(int __attribute__((noescape)) a, int b); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} + +struct S { int *p; }; +void test3(S __attribute__((noescape)) s); + +#if !__has_feature(attribute_noescape_nonpointer) + #error "attribute_noescape_nonpointer should be supported" +#endif diff --git a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp index 0c5b0cc85897b..9bd3b9eb108aa 100644 --- a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp @@ -1,6 +1,8 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++98 %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 %s -D CPP11 +// RUN: %clang_cc1 -fsyntax-only -I%S/Inputs -verify -Wthread-safety %s +// RUN: %clang_cc1 -fsyntax-only -I%S/Inputs -verify -Wthread-safety -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -I%S/Inputs -verify -Wthread-safety -std=c++11 %s -D CPP11 + +#include #define LOCKABLE __attribute__ ((lockable)) #define SCOPED_LOCKABLE __attribute__ ((scoped_lockable)) @@ -340,6 +342,19 @@ int gb_testfn(int y){ return x; } +void gb_function_sys_macro() _SYS_GUARDED_BY(mu1); // \ + // expected-warning {{'_SYS_GUARDED_BY' attribute only applies to}} + +void gb_function_params_sys_macro(int gv_lvar _SYS_GUARDED_BY(mu1)); // \ + // expected-warning {{'_SYS_GUARDED_BY' attribute only applies to}} + +int gb_testfn_sys_macro(int y){ + int x _SYS_GUARDED_BY(mu1) = y; // \ + // expected-warning {{'_SYS_GUARDED_BY' attribute only applies to}} + return x; +} + + //2. Check argument parsing. // legal attribute arguments diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp index c6c93a27e4b96..e80b54b7c6967 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp @@ -52,3 +52,75 @@ void constant_id_string(unsigned idx) { unsafe_char = ""[1]; //expected-warning{{unsafe buffer access}} unsafe_char = ""[idx]; //expected-warning{{unsafe buffer access}} } + +typedef float Float4x4[4][4]; + +// expected-warning@+1 {{'matrix' is an unsafe buffer that does not perform bounds checks}} +float two_dimension_array(Float4x4& matrix, unsigned idx) { + // expected-warning@+1{{unsafe buffer access}} + float a = matrix[0][4]; + + a = matrix[0][3]; + + // expected-note@+1{{used in buffer access here}} + a = matrix[4][0]; + + a = matrix[idx][0]; // expected-note{{used in buffer access here}} + + a = matrix[0][idx]; //expected-warning{{unsafe buffer access}} + + a = matrix[idx][idx]; //expected-warning{{unsafe buffer access}} // expected-note{{used in buffer access here}} + + return matrix[1][1]; +} + +typedef float Float2x3x4[2][3][4]; +float multi_dimension_array(Float2x3x4& matrix) { + float *f = matrix[0][2]; + return matrix[1][2][3]; +} + +char array_strings[][11] = { + "Apple", "Banana", "Cherry", "Date", "Elderberry" +}; + +char array_string[] = "123456"; + +char access_strings() { + char c = array_strings[0][4]; + c = array_strings[3][10]; + c = array_string[5]; + return c; +} + +struct T { + int array[10]; +}; + +const int index = 1; + +constexpr int get_const(int x) { + if(x < 3) + return ++x; + else + return x + 5; +}; + +void array_indexed_const_expr(unsigned idx) { + // expected-note@+2 {{change type of 'arr' to 'std::array' to label it for hardening}} + // expected-warning@+1{{'arr' is an unsafe buffer that does not perform bounds checks}} + int arr[10]; + arr[sizeof(int)] = 5; + + int array[sizeof(T)]; + array[sizeof(int)] = 5; + array[sizeof(T) -1 ] = 3; + + int k = arr[6 & 5]; + k = arr[2 << index]; + k = arr[8 << index]; // expected-note {{used in buffer access here}} + k = arr[16 >> 1]; + k = arr[get_const(index)]; + k = arr[get_const(5)]; // expected-note {{used in buffer access here}} + k = arr[get_const(4)]; +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp new file mode 100644 index 0000000000000..e6a1bf45aac06 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp @@ -0,0 +1,476 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -Wno-all -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -fexperimental-bounds-safety-attributes -verify %s + +#include +#include + +namespace std { + +template +struct array { + const T *data() const noexcept; + T *data() noexcept; + size_t size() const noexcept; +}; + +template +struct basic_string { + const CharT *data() const noexcept; + CharT *data() noexcept; + const CharT *c_str() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; +}; + +typedef basic_string string; + +template +struct basic_string_view { + const CharT *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; +}; + +typedef basic_string_view string_view; + +template +struct span { + T *data() const noexcept; + size_t size() const noexcept; + size_t size_bytes() const noexcept; + span first(size_t count) const noexcept; + span last(size_t count) const noexcept; + span subspan(size_t offset, size_t count) const noexcept; +}; + +template +struct vector { + const T *data() const noexcept; + T *data() noexcept; + size_t size() const noexcept; +}; + +} // namespace std + +template +struct my_vec { + const T *data() const noexcept; + T *data() noexcept; + size_t size() const noexcept; +}; + +extern "C" { + +// expected-note@+1 15{{consider using a safe container and passing '.data()' to the parameter 's' and '.size()' to its dependent parameter 'len' or 'std::span' and passing '.first(...).data()' to the parameter 's'}} +void cb_char(char *__counted_by(len) s, size_t len); + +void cb_cchar(const char *__counted_by(len) s, size_t len); + +// expected-note@+1 3{{consider using 'std::span' and passing '.first(...).data()' to the parameter 's'}} +void cb_cchar_42(const char *__counted_by(42) s); + +// expected-note@+1 19{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void cb_int(int *__counted_by(count) p, size_t count); + +// expected-note@+1 11{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void cb_cint(const int *__counted_by(count) p, size_t count); + +// expected-note@+1 10{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void cb_cint_42(const int *__counted_by(42) p); + +// expected-note@+1 6{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void cb_cint_multi(const int *__counted_by((a + b) * (c - d)) p, int a, int b, int c, int d); + +// expected-note@+1 11{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void sb_cvoid(const void *__sized_by(size) p, size_t size); + +// expected-note@+1 {{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void sb_cchar(const char *__sized_by(size) p, size_t size); + +// expected-note@+1 5{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void sb_cvoid_42(const void *__sized_by(42) p); + +// expected-note@+1 5{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void sb_cint(const int *__sized_by(size) p, size_t size); + +// expected-note@+1 3{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +void sb_cint_42(const int *__sized_by(42) p); + +void cb_cint_array(const int (* __counted_by(size) p)[10], size_t size); + +} // extern "C" + +// Check allowed classes and method. + +void allowed_class_and_method(std::array &a, std::string &s, + std::string_view sv, std::span sp, + std::vector &v) { + cb_int(a.data(), a.size()); + cb_cint(a.data(), a.size()); + + cb_char(s.data(), s.size()); + cb_char(s.data(), s.length()); + cb_cchar(s.data(), s.size()); + cb_cchar(s.data(), s.length()); + cb_cchar(s.c_str(), s.size()); + cb_cchar(s.c_str(), s.length()); + + cb_cchar(sv.data(), sv.size()); + cb_cchar(sv.data(), sv.length()); + + cb_cint(sp.data(), sp.size()); + sb_cvoid(sp.data(), sp.size_bytes()); + sb_cint(sp.data(), sp.size_bytes()); + + cb_int(v.data(), v.size()); + cb_cint(v.data(), v.size()); +} + +void not_allowed_class_and_method(my_vec &mv) { + cb_int(mv.data(), mv.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +// Check if the object argument is the same for .data() and .size(). + +void object_arg(std::span a, std::span b) { + cb_cint(a.data(), a.size()); + cb_cint(a.data(), b.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +// Check passing to pointers of different type has the correct .size() method. + +static_assert(sizeof(char) == 1); + +void pointers_of_diff_type(std::array &a, std::string &s, + std::span sp_int, std::span sp_char) { + cb_cint(a.data(), a.size()); + sb_cvoid(a.data(), a.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cchar(s.data(), s.size()); + sb_cvoid(s.data(), s.size()); + + cb_cint(sp_int.data(), sp_int.size()); + cb_cint(sp_int.data(), sp_int.size_bytes()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(sp_int.data(), sp_int.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(sp_int.data(), sp_int.size_bytes()); + sb_cint(sp_int.data(), sp_int.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint(sp_int.data(), sp_int.size_bytes()); + + cb_cchar(sp_char.data(), sp_char.size()); + cb_cchar(sp_char.data(), sp_char.size_bytes()); + sb_cvoid(sp_char.data(), sp_char.size()); + sb_cvoid(sp_char.data(), sp_char.size_bytes()); +} + +// Check if passing to __counted_by(const) uses a subview. + +void cb_const_subview(std::string &s, std::span sp) { + cb_cchar_42(s.data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cchar_42(s.c_str()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cint_42(sp.data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(42).data()); + cb_cint_42(sp.last(42).data()); + cb_cint_42(sp.subspan(0, 42).data()); +} + +// Check if the subview has a type with a correct size. + +static_assert(sizeof(char) == 1); + +void pointers_of_diff_type_subview(std::span sp_int, + std::span sp_char) { + cb_cint_42(sp_int.first(42).data()); + sb_cint_42(sp_int.first(42).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid_42(sp_int.first(42).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cchar_42(sp_char.first(42).data()); + sb_cvoid_42(sp_char.first(42).data()); +} + +// Check if subspan()'s offset doesn't matter. + +void subspan_offset(std::span sp, size_t x) { + cb_cint_42(sp.subspan(0, 42).data()); + cb_cint_42(sp.subspan(1337, 42).data()); + cb_cint_42(sp.subspan(x, 42).data()); + cb_cint_42(sp.subspan(sp.size() - x, 42).data()); +} + +// Check count expression. + +void count_expr_const(std::span sp, size_t x) { + cb_cint_42(sp.first(42).data()); + cb_cint_42(sp.first((42)).data()); + cb_cint_42(sp.first(0).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(41).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(43).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(10 + 10).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(x).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(sp.size()).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(sp.first(42 + x).data()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void count_expr_simple(std::span sp, size_t x) { + cb_cint(sp.first(42).data(), 42); + cb_cint(sp.first((42)).data(), 42); + cb_cint(sp.first(42).data(), (42)); + cb_cint(sp.first(((42))).data(), (((42)))); + cb_cint(sp.first(x).data(), x); + cb_cint(sp.first(42 + x).data(), 42 + x); + cb_cint(sp.first(x * x).data(), x * x); + + cb_cint(sp.first(-1).data(), -1); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(42).data(), 100); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(x).data(), 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(42).data(), x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(sp.size()).data(), x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(x).data(), sp.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(42 + x).data(), 42 - x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint(sp.first(x * x).data(), x + x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void count_expr_multi(std::span sp, int w, int x, int y, int z) { + cb_cint_multi(sp.first((4 + 3) * (2 - 1)).data(), 4, 3, 2, 1); + cb_cint_multi(sp.first((w + x) * (y - z)).data(), w, x, y, z); + cb_cint_multi(sp.first((1 + x) * (y - 2)).data(), 1, x, y, 2); + cb_cint_multi(sp.first(((1 + 2) + (1 + w)) * ((x + 2) - (y + z))).data(), 1 + 2, 1 + w, x + 2, y + z); + + cb_cint_multi(sp.first((4 + 3) * (2 - 1)).data(), 4, 3, 2, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_multi(sp.first((w + x) * (y - z)).data(), w, x, y, z + z); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_multi(sp.first((w + x) * (y + z)).data(), w, x, y, z); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +// Check passing arrays. + +void const_array(size_t x) { + int a[42], b[10]; + + cb_int(a, 42); + cb_int(a, 10); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(a, 100); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(a, x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(a, sizeof(a)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cint_42(a); + cb_cint_42(b); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + sb_cvoid(a, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint(a, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid_42(a); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint_42(a); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint(a, sizeof(a)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(a,sizeof(a)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid((void *)a, sizeof(a)); + sb_cvoid((char *)a, sizeof(a)); + sb_cchar((char *)a, sizeof(a)); + + char c[42]; + + cb_char(c, 42); + cb_char(c, 10); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(c, 100); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(c, x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cchar_42(c); + + sb_cvoid(c, 42); + sb_cvoid_42(c); + sb_cvoid(c, sizeof(c)); + sb_cvoid(c, sizeof(decltype(c))); + sb_cvoid(c, sizeof(char[42])); + +} + +void other_arrays(size_t n) { + int vla[n]; + extern int incomplete_array[]; + cb_int(vla, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(incomplete_array, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +// Count-attributed to count-attributed. + +void from_cb_int(int *__counted_by(len) p, size_t len) { + cb_int(p, len); + cb_cint(p, len); + cb_cint(p, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_42(p); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint(p, len); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(p, len); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void from_cb_int_42(int *__counted_by(42) p, size_t x) { + cb_int(p, 42); + cb_cint(p, 42); + cb_cint_42(p); + cb_int(p, 10); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, 100); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, x); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint(p, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(p, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cint_42(p); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid_42(p); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void from_cb_char(char *__counted_by(len) s, size_t len) { + cb_char(s, len); + cb_cchar(s, len); + sb_cvoid(s, len); + cb_cchar_42(s); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid_42(s); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void from_sb_void(void *__sized_by(size) p, size_t size) { + sb_cvoid(p, size); + sb_cvoid_42(p); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void from_cb_int_multi(int *__counted_by((a + b) * (c - d)) p, int a, int b, int c, int d) { + cb_cint_multi(p, a, b, c, d); + cb_cint_multi(p, d, c, b, a); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_multi(p, 1, 2, 3, 4); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_cint_multi(p, a, b, 42, d); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_int(p, (a + b) * (c - d)); + cb_int(p, (d + c) * (b - a)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, (a - b) * (c + d)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, (a + 42) * (c - d)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, a + b); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, a); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(p, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void nullptr_as_arg(void * __sized_by(size) p, unsigned size) { //expected-note{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} + nullptr_as_arg(nullptr, 0); + nullptr_as_arg(nullptr, size); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +void single_variable() { + int Var; + int Arr[10]; + + cb_int(&Var, 1); + sb_cvoid(&Var, sizeof(int)); + sb_cvoid(&Var, sizeof(Var)); + sb_cvoid(&Var, sizeof(decltype(Var))); + sb_cchar((char*)&Var, 1); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(&Var, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(&Var, sizeof(Arr)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(&Var, sizeof(decltype(Arr))); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + + cb_cint_array(&Arr, 1); + sb_cvoid(&Arr, sizeof(Arr)); + sb_cvoid(&Arr, sizeof(int[10])); + sb_cvoid(&Arr, sizeof(decltype(Arr))); + sb_cvoid(&Arr, sizeof(Var)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(&Arr, sizeof(int[11])); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + sb_cvoid(&Arr, sizeof(decltype(Var))); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} +} + +namespace test_member_access { + class Base { + public: + char * __counted_by(n) p; + size_t n; + + char * __counted_by(n + m) q; + size_t m; + + void foo() { + cb_char(p, n); // no warn + cb_char(p, m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(q, n + m); // no warn + cb_char(q, m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + + void bar(size_t n) { // parameter n makes Base::n invisible + cb_char(p, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(q, n + m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + }; + + class Derived : public Base { + void foo() { + cb_char(p, n); // no warn + cb_char(p, m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(q, n + m); // no warn + cb_char(q, m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + + void bar(size_t n) { + cb_char(p, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(q, n + m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + }; + + + class Derived2 : public Base { + size_t n; // Derived2::n makes Base::n invisible + void foo() { + cb_char(p, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(q, n + m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + }; + + void memberAccessArgs(Base B, Base B2) { + cb_char(B.p, B.n); // no warn + cb_char(B.p, B2.n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_char(B.q, B.n + B.m); // no warn + cb_char(B.p, B.n + B2.m); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + + class MyClass { + void test_brackets(int p[__counted_by(n)], size_t n, int * q[__counted_by(n)]) { + cb_int(p, n); + cb_int(p, 10); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + cb_int(*q, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}} + } + }; +} // test_member_access + +namespace output_param_test { + void cb_out_ptr(int * __counted_by(n) * p, size_t n); + void cb_out_count(int * __counted_by(*n) p, size_t * n); + void cb_out_both(int * __counted_by(*n) * p, size_t * n); + + void test(int * __counted_by(n) p, size_t n, int * __counted_by(*m) *q, size_t *m) { + cb_out_ptr(&p, n); // no error + cb_out_ptr(&p, *&n); // no error + cb_out_ptr(q, *m); // no error + cb_out_count(p, &n); // no error + cb_out_count(*q, m); // no error + cb_out_count(*q, *&m); // no error + // The following pattern is a bounds-safety error for read-only parameter 'p': + // cb_out_both(&p, &n); + cb_out_both(q, m); // no error + + size_t local_n = n; + int * __counted_by(local_n) local_p = p; + + cb_out_ptr(&local_p, local_n); // no error + cb_out_count(local_p, &local_n); // no error + cb_out_both(&local_p, &local_n); // no error + } + + class TestClassMemberFunctions { + void test(int * __counted_by(n) p, size_t n, int * __counted_by(*m) *q, size_t *m) { + cb_out_ptr(&p, n); // no error + cb_out_ptr(&p, *&n); // no error + cb_out_ptr(q, *m); // no error + cb_out_count(p, &n); // no error + cb_out_count(*q, m); // no error + cb_out_count(*q, *&m); // no error + // The following pattern is a bounds-safety error for read-only parameter 'p': + // cb_out_both(&p, &n); + cb_out_both(q, m); // no error + + size_t local_n = n; + int * __counted_by(local_n) local_p = p; + + cb_out_ptr(&local_p, local_n); // no error + cb_out_count(local_p, &local_n); // no error + cb_out_both(&local_p, &local_n); // no error + } + }; + +} // namespace output_param_test diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-field-attr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-field-attr.cpp index 0ba605475925b..1636c948da075 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-field-attr.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-field-attr.cpp @@ -96,7 +96,6 @@ void test_attribute_multiple_fields (D d) { int v = d.buf[0]; //expected-warning{{field 'buf' prone to unsafe buffer manipulation}} - //expected-warning@+1{{unsafe buffer access}} v = d.buf[5]; //expected-warning{{field 'buf' prone to unsafe buffer manipulation}} } diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp index 71350098613d1..0c80da63f8291 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp @@ -118,7 +118,7 @@ void isArrayDecayToPointerUPC(int a[][10], int (*b)[10]) { // expected-warning@-2{{'b' is an unsafe pointer used for buffer access}} int tmp; - tmp = a[5][5] + b[5][5]; // expected-warning2{{unsafe buffer access}} expected-note2{{used in buffer access here}} + tmp = a[5][5] + b[5][5]; // expected-note2{{used in buffer access here}} } // parameter having default values: diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct-count-attributed-pointer.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct-count-attributed-pointer.cpp new file mode 100644 index 0000000000000..39802a1200d14 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct-count-attributed-pointer.cpp @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-container -fexperimental-bounds-safety-attributes -verify %s + +#include +#include + +namespace std { + +template +struct span { + constexpr span(T *, size_t) {} +}; + +} // namespace std + +struct cb { + size_t len; + int *__counted_by(len) p; +}; + +void span_from_cb(cb *c, cb *d, size_t len) { + std::span{c->p, c->len}; + std::span{c->p, 42}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, d->len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, c->len + 1}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +struct cb_const { + int *__counted_by(42) p; +}; + +void span_from_cb_const(cb_const *c, size_t len) { + std::span{c->p, 42}; + std::span{c->p, 43}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, 42 + len}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +struct cb_multi { + size_t a, b, c, d; + int *__counted_by((a + b) * (c - d)) p; +}; + +void span_from_cb_multi(cb_multi *c, cb_multi *d, size_t len) { + std::span{c->p, (c->a + c->b) * (c->c - c->d)}; + std::span{c->p, (c->a + c->b) * (c->c + c->d)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, (c->a + c->b) + (c->c - c->d)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, c->a + c->b}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, 42}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, (c->a + c->b) * (42 - c->d)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, (d->a + d->b) * (d->c - d->d)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, (c->a + d->b) * (c->c - c->d)}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +int *__counted_by(len) fn_cb(size_t len); + +void span_from_fn_cb(cb *c, size_t len, size_t len2) { + std::span{fn_cb(len), len}; + std::span{fn_cb(len), 42}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb(len), len2}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb(len), c->len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb(len), len + 1}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +int *__counted_by(42) fn_cb_const(); + +void span_from_fn_cb_const(cb *c, size_t len) { + std::span{fn_cb_const(), 42}; + std::span{fn_cb_const(), 43}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_const(), len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_const(), c->len}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_const(), 42 + 1}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +int *__counted_by((a + b) * (c - d)) fn_cb_multi(size_t a, size_t b, size_t c, size_t d); + +void span_from_fn_cb_multi(cb *c, size_t w, size_t x, size_t y, size_t z) { + std::span{fn_cb_multi(4, 3, 2, 1), (4 + 3) * (2 - 1)}; + std::span{fn_cb_multi(w, x, y, z), (w + x) * (y - z)}; + std::span{fn_cb_multi(1, x, y, 2), (1 + x) * (y - 2)}; + std::span{fn_cb_multi(x, x, x, x), (x + x) * (x - x)}; + std::span{fn_cb_multi((1+2), (1+w), (x+2), (y+z)), ((1+2) + (1+w)) * ((x+2) - (y+z))}; + std::span{fn_cb_multi(4, 3, 2, 1), (4 + 3) * (42 - 1)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_multi(4, 3, 2, 1), (4 + 3) * (x - 1)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_multi(4, 3, x, 1), (4 + 3) * (42 - 1)}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_multi(w, x, y, z), (w + x) * (y - (z + z))}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{fn_cb_multi(w, x, y, z), (w + x) * (y + z)}; // expected-warning{{the two-parameter std::span construction is unsafe}} +} + +struct sb_int { + size_t size; + int *__sized_by(size) p; +}; + +struct sb_char { + size_t size; + char *__sized_by(size) p; +}; + +void span_from_sb_int(sb_int *i, sb_char *c) { + std::span{i->p, i->size}; // expected-warning{{the two-parameter std::span construction is unsafe}} + std::span{c->p, c->size}; +} + +void span_from_output_parm(int * __counted_by(n) *cb_p, size_t n, + int * __counted_by(*m) *cb_q, size_t *m, + int * __counted_by(*l) cb_z, size_t *l, + char * __sized_by(n) *sb_p, + char * __sized_by(*m) *sb_q, + char * __sized_by(*l) sb_z) { + std::span(*cb_p, n); // no warn + std::span(*cb_q, *m); // no warn + std::span(cb_z, *l); // no warn + std::span(*sb_p, n); // no warn + std::span(*sb_q, *m); // no warn + std::span(sb_z, *l); // no warn + std::span(*cb_q, n); // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}} +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp index 014aae0e8a4c0..b0174a1e854c0 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct.cpp @@ -21,6 +21,12 @@ namespace std { template< class T > T&& move( T&& t ) noexcept; + + template + _Tp* addressof(_Tp& __x) { + return &__x; + } + } namespace irrelevant_constructors { @@ -74,13 +80,24 @@ namespace construct_wt_ptr_size { return std::span{p, 10}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}} } + // addressof method defined outside std namespace. + template + _Tp* addressof(_Tp& __x) { + return &__x; + } + void notWarnSafeCases(unsigned n, int *p) { int X; unsigned Y = 10; std::span S = std::span{&X, 1}; // no-warning + S = std::span{std::addressof(X), 1}; // no-warning int Arr[10]; S = std::span{&X, 2}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}} + S = std::span{std::addressof(X), 2}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}} + // Warn when a non std method also named addressof + S = std::span{addressof(X), 1}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}} + S = std::span{new int[10], 10}; // no-warning S = std::span{new int[n], n}; // no-warning S = std::span{new int, 1}; // no-warning diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp index a7c19bcac1607..98a4c7bc6943e 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp @@ -84,6 +84,9 @@ void f(char * p, char * q, std::span s, std::span s2) { snprintf(q, 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}} snprintf(cp, 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}} snprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}} + snprintf(nullptr, 0, ""); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}} + printf(nullptr); // expected-warning{{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} + printf("%s", nullptr); // expected-warning{{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} snwprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{buffer pointer and size may not match}} snwprintf_s( // expected-warning{{function 'snwprintf_s' is unsafe}} s.data(), // expected-note{{buffer pointer and size may not match}} // note attached to the buffer diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp index 642db0e9d3c63..41d38ada48788 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp @@ -109,7 +109,6 @@ void testQualifiedParameters(const int * p, const int * const q, const int a[10] q[1], 1[q], q[-1], // expected-note3{{used in buffer access here}} a[1], // expected-note{{used in buffer access here}} `a` is of pointer type b[1][2] // expected-note{{used in buffer access here}} `b[1]` is of array type - // expected-warning@-1{{unsafe buffer access}} ); } @@ -128,29 +127,41 @@ T_t funRetT(); T_t * funRetTStar(); void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) { - foo(sp->a[1], // expected-warning{{unsafe buffer access}} + foo(sp->a[1], sp->b[1], // expected-warning{{unsafe buffer access}} - sp->c.a[1], // expected-warning{{unsafe buffer access}} + sp->c.a[1], sp->c.b[1], // expected-warning{{unsafe buffer access}} - s.a[1], // expected-warning{{unsafe buffer access}} + s.a[1], s.b[1], // expected-warning{{unsafe buffer access}} - s.c.a[1], // expected-warning{{unsafe buffer access}} + s.c.a[1], s.c.b[1], // expected-warning{{unsafe buffer access}} - sp2->a[1], // expected-warning{{unsafe buffer access}} + sp2->a[1], sp2->b[1], // expected-warning{{unsafe buffer access}} - sp2->c.a[1], // expected-warning{{unsafe buffer access}} + sp2->c.a[1], sp2->c.b[1], // expected-warning{{unsafe buffer access}} - s2.a[1], // expected-warning{{unsafe buffer access}} + s2.a[1], s2.b[1], // expected-warning{{unsafe buffer access}} - s2.c.a[1], // expected-warning{{unsafe buffer access}} + s2.c.a[1], s2.c.b[1], // expected-warning{{unsafe buffer access}} - funRetT().a[1], // expected-warning{{unsafe buffer access}} + funRetT().a[1], funRetT().b[1], // expected-warning{{unsafe buffer access}} - funRetTStar()->a[1], // expected-warning{{unsafe buffer access}} + funRetTStar()->a[1], funRetTStar()->b[1] // expected-warning{{unsafe buffer access}} ); } +union Foo { + bool b; + int arr[10]; +}; + +int testUnionMembers(Foo f) { + int a = f.arr[0]; + a = f.arr[5]; + a = f.arr[10]; // expected-warning{{unsafe buffer access}} + return a; +} + int garray[10]; // expected-warning{{'garray' is an unsafe buffer that does not perform bounds checks}} int * gp = garray; // expected-warning{{'gp' is an unsafe pointer used for buffer access}} int gvar = gp[1]; // FIXME: file scope unsafe buffer access is not warned @@ -213,7 +224,6 @@ void testTypedefs(T_ptr_t p) { // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} foo(p[1], // expected-note{{used in buffer access here}} p[1].a[1], // expected-note{{used in buffer access here}} - // expected-warning@-1{{unsafe buffer access}} p[1].b[1] // expected-note{{used in buffer access here}} // expected-warning@-1{{unsafe buffer access}} ); @@ -223,10 +233,9 @@ template T f(T t, T * pt, T a[N], T (&b)[N]) { // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}} // expected-warning@-2{{'pt' is an unsafe pointer used for buffer access}} // expected-warning@-3{{'a' is an unsafe pointer used for buffer access}} - // expected-warning@-4{{'b' is an unsafe buffer that does not perform bounds checks}} foo(pt[1], // expected-note{{used in buffer access here}} a[1], // expected-note{{used in buffer access here}} - b[1]); // expected-note{{used in buffer access here}} + b[1]); return &t[1]; // expected-note{{used in buffer access here}} } @@ -376,7 +385,7 @@ int testArrayAccesses(int n, int idx) { typedef int A[3]; const A tArr = {4, 5, 6}; foo(tArr[0], tArr[1]); - return cArr[0][1]; // expected-warning{{unsafe buffer access}} + return cArr[0][1]; } void testArrayPtrArithmetic(int x[]) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} diff --git a/clang/test/SemaObjC/method-param-named-id.m b/clang/test/SemaObjC/method-param-named-id.m new file mode 100644 index 0000000000000..8269c31116c32 --- /dev/null +++ b/clang/test/SemaObjC/method-param-named-id.m @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s + + +@interface Foo +-(void)paramNamedID:(int)id usesIDType:(id)notShadowed; +-(void)paramNamedID:(int)id, id notShadowed; // expected-warning{{use of C-style parameters in Objective-C method declarations is deprecated}} +@end diff --git a/clang/test/SemaObjCXX/noescape.mm b/clang/test/SemaObjCXX/noescape.mm index 4b52164dffd3d..74290daf17426 100644 --- a/clang/test/SemaObjCXX/noescape.mm +++ b/clang/test/SemaObjCXX/noescape.mm @@ -17,17 +17,17 @@ void noescapeFunc2(int *); // expected-error {{conflicting types for 'noescapeFunc2'}} template -void noescapeFunc5(__attribute__((noescape)) T); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +void noescapeFunc5(__attribute__((noescape)) T); template void noescapeFunc6(__attribute__((noescape)) const T &); template void noescapeFunc7(__attribute__((noescape)) T &&); -void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} void invalidFunc1(int __attribute__((noescape(0)))); // expected-error {{'noescape' attribute takes no arguments}} void invalidFunc2(int0 *__attribute__((noescape))); // expected-error {{use of undeclared identifier 'int0'; did you mean 'int'?}} -void invalidFunc3(__attribute__((noescape)) int (S::*Ty)); // expected-warning {{'noescape' attribute only applies to pointer arguments}} -void invalidFunc4(__attribute__((noescape)) void (S::*Ty)()); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +void invalidFunc3(__attribute__((noescape)) int (S::*Ty)); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} +void invalidFunc4(__attribute__((noescape)) void (S::*Ty)()); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} int __attribute__((noescape)) g; // expected-warning {{'noescape' attribute only applies to parameters}} struct S1 { @@ -144,7 +144,7 @@ -(void) m1:(int*) p { struct S6 { S6(); - S6(const S6 &) = delete; // expected-note 12 {{'S6' has been explicitly marked deleted here}} + S6(const S6 &) = delete; // expected-note 11 {{'S6' has been explicitly marked deleted here}} int f; }; @@ -161,7 +161,7 @@ void test1(C0 *c0) { __block S6 b6; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b7; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b8; // expected-error {{call to deleted constructor of 'S6'}} - __block S6 b9; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b9; __block S6 b10; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b11; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b12; @@ -199,7 +199,6 @@ void test1(C0 *c0) { (void)b8; }); - // FIXME: clang shouldn't reject this. noescapeFunc5(^{ (void)b9; }); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 6ba80f72bda57..13a8e6d3a5a28 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1781,6 +1781,17 @@ bool CursorVisitor::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) { return Visit(TL.getInnerLoc()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool CursorVisitor::VisitDynamicRangePointerTypeLoc( + DynamicRangePointerTypeLoc TL) { + return Visit(TL.getInnerLoc()); +} + +bool CursorVisitor::VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + return Visit(TL.getOriginalLoc()); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { return Visit(TL.getWrappedLoc()); } @@ -5890,6 +5901,10 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("ObjCMessageExpr"); case CXCursor_BuiltinBitCastExpr: return cxstring::createRef("BuiltinBitCastExpr"); + case CXCursor_ForgePtrExpr: + return cxstring::createRef("ForgePtrExpr"); + case CXCursor_GetBoundExpr: + return cxstring::createRef("GetBoundExpr"); case CXCursor_ConceptSpecializationExpr: return cxstring::createRef("ConceptSpecializationExpr"); case CXCursor_RequiresExpr: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 782c0c243ef1f..9ff6a80ae3c6f 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -329,6 +329,13 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::SourceLocExprClass: case Stmt::ConvertVectorExprClass: case Stmt::VAArgExprClass: + case Stmt::BoundsCheckExprClass: + case Stmt::PredefinedBoundsCheckExprClass: + case Stmt::AssumptionExprClass: + case Stmt::BoundsSafetyPointerPromotionExprClass: + case Stmt::MaterializeSequenceExprClass: + case Stmt::TerminatedByToIndexableExprClass: + case Stmt::TerminatedByFromIndexableExprClass: case Stmt::ObjCArrayLiteralClass: case Stmt::ObjCDictionaryLiteralClass: case Stmt::ObjCBoxedExprClass: @@ -640,6 +647,13 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, return getSelectorIdentifierCursor(SelectorIdIndex, C); } + case Stmt::ForgePtrExprClass: + K = CXCursor_ForgePtrExpr; + break; + case Stmt::GetBoundExprClass: + K = CXCursor_GetBoundExpr; + break; + case Stmt::ConceptSpecializationExprClass: K = CXCursor_ConceptSpecializationExpr; break; diff --git a/clang/unittests/Tooling/BoundsSafetyBringupMissingChecks.cpp b/clang/unittests/Tooling/BoundsSafetyBringupMissingChecks.cpp new file mode 100644 index 0000000000000..20f2977a99733 --- /dev/null +++ b/clang/unittests/Tooling/BoundsSafetyBringupMissingChecks.cpp @@ -0,0 +1,402 @@ +//=== unittests/Tooling/BoundsSafetyBringupMissingChecks.cpp =================// +// +// 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 test is really a `Frontend` test but it's in `Tooling` so we can use +// `clang::tooling::runToolOnCodeWithArgs` to make writing the unit test much +// easier. +// +// This test verifies LangOptions gets set appropriately based on the provided +// `-fbounds-safety-bringup-missing-checks` flags. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Sema/Sema.h" +#include "clang/Tooling/Tooling.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace clang::tooling; + +// FIXME: These functions are a workaround for +// `clang::tooling::runToolOnCodeWithArgs` not returning false when handling +// unrecognized driver flags (rdar://138379948). This isn't the right fix. +// The radar explains the correct way to fix this. +static std::vector +getSyntaxOnlyToolArgs(const Twine &ToolName, + const std::vector &ExtraArgs, + StringRef FileName) { + std::vector Args; + Args.push_back(ToolName.str()); + Args.push_back("-fsyntax-only"); + Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end()); + Args.push_back(FileName.str()); + return Args; +} + +static bool +runToolOnCodeWithArgs(std::unique_ptr ToolAction, + const Twine &Code, + llvm::IntrusiveRefCntPtr VFS, + const std::vector &Args, + const Twine &FileName, const Twine &ToolName, + std::shared_ptr PCHContainerOps = + std::make_shared()) { + SmallString<16> FileNameStorage; + StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); + + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), VFS)); + ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster(); + ToolInvocation Invocation( + getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef), + std::move(ToolAction), Files.get(), std::move(PCHContainerOps)); + + // Workaround rdar://138379948. Note this isn't the right way to fix the bug + // but it's good enough for the purposes of this test. + std::vector ArgsCC; + for (const auto &Arg : Args) + ArgsCC.push_back(Arg.c_str()); + IntrusiveRefCntPtr DiagOpts = + clang::CreateAndPopulateDiagOpts(ArgsCC); + auto TDP = + std::make_unique(llvm::errs(), DiagOpts.get()); + Invocation.setDiagnosticConsumer(TDP.get()); + + return Invocation.run() && TDP->getNumErrors() == 0; +} + +static bool runToolOnCodeWithArgs( + std::unique_ptr ToolAction, const Twine &Code, + const std::vector &Args, const Twine &FileName, + const Twine &ToolName = "clang-tool", + std::shared_ptr PCHContainerOps = + std::make_shared(), + const FileContentMappings &VirtualMappedFiles = FileContentMappings()) { + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); + + SmallString<1024> CodeStorage; + InMemoryFileSystem->addFile(FileName, 0, + llvm::MemoryBuffer::getMemBuffer( + Code.toNullTerminatedStringRef(CodeStorage))); + + for (auto &FilenameWithContent : VirtualMappedFiles) { + InMemoryFileSystem->addFile( + FilenameWithContent.first, 0, + llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second)); + } + + return ::runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem, + Args, FileName, ToolName); +} + +using LangOptionsTestFn = std::function; + +bool runOnToolAndCheckLangOptions(const std::vector &Args, + LangOptionsTestFn Handler) { + class CheckLangOptions : public clang::InitOnlyAction { + LangOptionsTestFn Handler; + + public: + CheckLangOptions(LangOptionsTestFn Handler) : Handler(Handler) {} + bool BeginSourceFileAction(CompilerInstance &CI) override { + auto &LangOpts = getCompilerInstance().getLangOpts(); + Handler(LangOpts); + return true; + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return std::make_unique(); + } + }; + + auto Action = std::make_unique(Handler); + // FIXME: Workaround invalid driver args not causing false to be returned + // (rdar://138379948) by calling a modified version of + // `runToolOnCodeWithArgs`. + bool Result = /*clang::tooling*/ ::runToolOnCodeWithArgs( + std::move(Action), /*Code=*/"", Args, "test.c"); + + return Result; +} + +struct ChkPair { + const char *arg; + unsigned Mask; +}; + +ChkPair Pairs[] = { + {"access_size", LangOptions::BS_CHK_AccessSize}, + {"indirect_count_update", LangOptions::BS_CHK_IndirectCountUpdate}, + {"return_size", LangOptions::BS_CHK_ReturnSize}, + {"ended_by_lower_bound", LangOptions::BS_CHK_EndedByLowerBound}, + {"compound_literal_init", LangOptions::BS_CHK_CompoundLiteralInit}, + {"libc_attributes", LangOptions::BS_CHK_LibCAttributes}}; +const size_t NumPairs = sizeof(Pairs) / sizeof(Pairs[0]); +static_assert(NumPairs == 6, "Unexpected value"); + +// Check that `Pairs` is in sync with the `BoundsSafetyNewChecksMask` +// enum. +TEST(BoundsSafetyBringUpMissingChecks, ChkPairInSync) { + unsigned ComputedMask = 0; + static_assert(LangOptions::BS_CHK_None == 0, "expected 0"); + for (size_t Idx = 0; Idx < NumPairs; ++Idx) { + ComputedMask |= Pairs[Idx].Mask; + } + ASSERT_EQ(ComputedMask, LangOptions::BS_CHK_All); +} + +TEST(BoundsSafetyBringUpMissingChecks, ChkPairValidMask) { + unsigned SeenBits = 0; + static_assert(LangOptions::BS_CHK_None == 0, "expected 0"); + for (size_t Idx = 0; Idx < NumPairs; ++Idx) { + unsigned CurrentMask = Pairs[Idx].Mask; + EXPECT_EQ(__builtin_popcount(CurrentMask), 1); // Check is a power of 2 + EXPECT_EQ(SeenBits & CurrentMask, + 0U); // Doesn't overlap with a previously seen value + SeenBits |= CurrentMask; + EXPECT_NE(SeenBits, 0U); + } + ASSERT_EQ(SeenBits, LangOptions::BS_CHK_All); +} + +// ============================================================================= +// Default behavior +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, DefaultWithBoundsSafety) { + bool Result = + runOnToolAndCheckLangOptions({"-fbounds-safety"}, [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_Default); + }); + ASSERT_TRUE(Result); +} + +TEST(BoundsSafetyBringUpMissingChecks, DefaultWithoutBoundsSafety) { + bool Result = + runOnToolAndCheckLangOptions({"-fno-bounds-safety"}, [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_Default); + }); + ASSERT_TRUE(Result); + bool Result2 = runOnToolAndCheckLangOptions({""}, [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_Default); + }); + ASSERT_TRUE(Result2); +} + +TEST(BoundsSafetyBringUpMissingChecks, + DefaultAllDisabledExperimentalBoundsSafetyAttributes) { + bool Result = runOnToolAndCheckLangOptions( + {"-fexperimental-bounds-safety-attributes"}, [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_Default); + }); + ASSERT_TRUE(Result); +} + +// ============================================================================= +// All checks enabled +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, all_eq) { + bool Result = runOnToolAndCheckLangOptions( + {"-fbounds-safety", "-fbounds-safety-bringup-missing-checks=all"}, + [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_All); + + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)Pairs[ChkIdx].Mask)); + } + }); + ASSERT_TRUE(Result); +} + +TEST(BoundsSafetyBringUpMissingChecks, all) { + bool Result = runOnToolAndCheckLangOptions( + {"-fbounds-safety", "-fbounds-safety-bringup-missing-checks"}, + [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_All); + + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)Pairs[ChkIdx].Mask)); + } + }); + ASSERT_TRUE(Result); +} + +// ============================================================================= +// Specify just one check +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, only_one_check) { + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + ChkPair Chk = Pairs[ChkIdx]; + std::vector Args = {"-fbounds-safety", + "-fbounds-safety-bringup-missing-checks="}; + Args[1].append(Chk.arg); + + bool Result = runOnToolAndCheckLangOptions(Args, [&Chk](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), Chk.Mask); + + // Check helper + for (size_t OtherChkIdx = 0; OtherChkIdx < NumPairs; ++OtherChkIdx) { + ChkPair OtherChk = Pairs[OtherChkIdx]; + if (OtherChk.Mask == Chk.Mask) { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } else { + EXPECT_FALSE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } + } + }); + ASSERT_TRUE(Result); + } +} + +// ============================================================================= +// Pairs of checks +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, all_pairs) { + // Construct all distinct pairs and test + for (size_t firstIdx = 0; firstIdx < NumPairs; ++firstIdx) { + for (size_t secondIdx = firstIdx + 1; secondIdx < NumPairs; ++secondIdx) { + ASSERT_NE(firstIdx, secondIdx); + ChkPair First = Pairs[firstIdx]; + auto Second = Pairs[secondIdx]; + std::vector Args = { + "-fbounds-safety", "-fbounds-safety-bringup-missing-checks="}; + Args[1].append(First.arg); + Args[1].append(","); + Args[1].append(Second.arg); + ASSERT_NE(First.Mask, Second.Mask); + + bool Result = runOnToolAndCheckLangOptions( + Args, [&First, &Second](LangOptions &LO) { + unsigned ExpectedMask = First.Mask | Second.Mask; + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), ExpectedMask); + + // Check helper + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + unsigned ChkToTest = Pairs[ChkIdx].Mask; + if (ChkToTest == First.Mask || ChkToTest == Second.Mask) { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)ChkToTest)); + } else { + EXPECT_FALSE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)ChkToTest)); + } + } + }); + ASSERT_TRUE(Result); + } + } +} + +// ============================================================================= +// All checks with one removed +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, all_with_one_removed) { + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + ChkPair Chk = Pairs[ChkIdx]; + std::vector Args = { + "-fbounds-safety", "-fbounds-safety-bringup-missing-checks=all", + "-fno-bounds-safety-bringup-missing-checks="}; + Args[2].append(Chk.arg); + + bool Result = runOnToolAndCheckLangOptions(Args, [&Chk](LangOptions &LO) { + unsigned ExpectedMask = LangOptions::BS_CHK_All & (~Chk.Mask); + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), ExpectedMask); + + // Check helper + for (size_t OtherChkIdx = 0; OtherChkIdx < NumPairs; ++OtherChkIdx) { + ChkPair OtherChk = Pairs[OtherChkIdx]; + if (OtherChk.Mask == Chk.Mask) { + EXPECT_FALSE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } else { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } + } + }); + ASSERT_TRUE(Result); + } +} + +// ============================================================================= +// No checks +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, all_disabled) { + bool Result = runOnToolAndCheckLangOptions( + {"-fbounds-safety", "-fno-bounds-safety-bringup-missing-checks"}, + [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_None); + }); + ASSERT_TRUE(Result); +} + +TEST(BoundsSafetyBringUpMissingChecks, all_enable_then_disable) { + bool Result = runOnToolAndCheckLangOptions( + {"-fbounds-safety", "-fbounds-safety-bringup-missing-checks", + "-fno-bounds-safety-bringup-missing-checks"}, + [](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), + LangOptions::BS_CHK_None); + }); + ASSERT_TRUE(Result); +} + +// ============================================================================= +// Explicitly disable all checks then enable one +// ============================================================================= + +TEST(BoundsSafetyBringUpMissingChecks, all_disabled_then_enable_one) { + for (size_t ChkIdx = 0; ChkIdx < NumPairs; ++ChkIdx) { + ChkPair Chk = Pairs[ChkIdx]; + std::vector Args = { + "-fbounds-safety", "-fno-bounds-safety-bringup-missing-checks", + "-fbounds-safety-bringup-missing-checks="}; + Args[2].append(Chk.arg); + + bool Result = runOnToolAndCheckLangOptions(Args, [&Chk](LangOptions &LO) { + EXPECT_EQ(LO.getBoundsSafetyBringUpMissingChecks(), Chk.Mask); + + // Check helper + for (size_t OtherChkIdx = 0; OtherChkIdx < NumPairs; ++OtherChkIdx) { + ChkPair OtherChk = Pairs[OtherChkIdx]; + if (OtherChk.Mask == Chk.Mask) { + EXPECT_TRUE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } else { + EXPECT_FALSE(LO.hasNewBoundsSafetyCheck( + (LangOptions::BoundsSafetyNewChecksMask)OtherChk.Mask)); + } + } + }); + ASSERT_TRUE(Result); + } +} + diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt index 3c1a4eaba66cc..31c29021d313b 100644 --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -10,6 +10,7 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(ToolingTests ASTSelectionTest.cpp + BoundsSafetyBringupMissingChecks.cpp CastExprTest.cpp CommentHandlerTest.cpp CompilationDatabaseTest.cpp diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index c5ac3fcb1e939..e3739876ebd4f 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1363,6 +1363,12 @@ namespace { {} }; + class VariadicDeclArgument : public VariadicArgument { + public: + VariadicDeclArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "Decl *") {} + }; + class VariadicStringArgument : public VariadicArgument { public: VariadicStringArgument(const Record &Arg, StringRef Attr) @@ -1502,6 +1508,8 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = std::make_unique(Arg, Attr, "ParamIdx"); else if (ArgName == "VariadicIdentifierArgument") Ptr = std::make_unique(Arg, Attr); + else if (ArgName == "VariadicDeclArgument") + Ptr = std::make_unique(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = std::make_unique(Arg, Attr); else if (ArgName == "WrappedAttr") diff --git a/clang/utils/simplify_ast_dump_for_checks.py b/clang/utils/simplify_ast_dump_for_checks.py new file mode 100644 index 0000000000000..15c3a082e9d8e --- /dev/null +++ b/clang/utils/simplify_ast_dump_for_checks.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +""" +A utility to simplify AST dumps to a format that is compatible with FileChecks. + +The tool tries to only keep essential information from AST dumps to make them +easier to read and complete while keeping them complete enough to match against. + +Usage: clang -Xclang -ast-dump foo.c | python simplify_ast_dump_for_checks.py +""" + +import sys + +DEBUG = False + +def char_iter(stream): + contents = stream.read(4000) + while contents: + for c in contents: + yield c + contents = stream.read(4000) + +def parse_indent(iter, nextval): + indent = "" + while nextval in "`-| ": + indent += nextval + nextval = next(iter) + return (nextval, indent) + +def parse_spaces(iter, nextval): + r = '' + while nextval.isspace() or nextval == ':': + r += nextval + nextval = next(iter) + return (nextval, r) + +def parse_tok(iter, nextval): + tok = "" + while not nextval.isspace(): + tok += nextval + nextval = next(iter) + return (nextval, tok) + +def parse_sloc(iter, nextval): + assert nextval == '<' + result = nextval + nextval = next(iter) + depth = 1 + while depth != 0: + if nextval == '<': + depth += 1 + elif nextval == '>': + depth -= 1 + result += nextval + nextval = next(iter) + return (nextval, result) + +def parse_quoted(iter, nextval): + assert nextval == "'" + result = nextval + nextval = next(iter) + depth = 1 + while depth != 0: + result += nextval + if nextval == "'": + depth -= 1 + nextval = next(iter) + return (nextval, result) + +def parse_any_tok(iter, nextval): + if nextval == "'": + return parse_quoted(iter, nextval) + elif nextval == '<': + return parse_sloc(iter, nextval) + else: + return parse_tok(iter, nextval) + +line_n = 0 +class AstNode(object): + def __init__(self, n, indent, kind, addr, *whatev): + self.depth = len(indent) / 2 + self.indent = indent + self.kind = kind + self.addr = int(addr, 0) + self.remainder = whatev + self.line_n = n + + def __repr__(self): + fields = ", ".join(repr(x) for x in (self.depth, self.kind, self.addr) + self.remainder) + return "%s(%s)" % (type(self).__name__, fields) + + @classmethod + def new(cls, n, payload): + try: + payload = list(payload) + if payload[1] == "array_filler:": + return ArrayFillerNode(cls, n, *payload) + if payload[1].endswith("VarDecl"): + # move "used" marker at the end for simplicity + try: + used_idx = payload.index("used") + except ValueError: + pass + else: + payload.append(payload[used_idx]) + del payload[used_idx] + if payload[1].endswith("CastExpr"): + # remove part_of_explicit_cast for simplicity + try: + marker_idx = payload.index("part_of_explicit_cast") + except ValueError: + pass + else: + del payload[marker_idx] + if payload[1].endswith("VarDecl"): + return VarDeclNode(n, *payload) + if payload[1] == "FunctionDecl": + return FuncDeclNode(n, *payload) + if payload[1] == "MemberExpr": + return MemberNode(n, *payload) + except TypeError: + pass + + return cls(n, *payload) + + @classmethod + def parse_line(cls, iter, nextval): + global line_n + nextval, indent = parse_indent(iter, nextval) + nextval, node_name = parse_any_tok(iter, nextval) + tokens = [indent, node_name] + while nextval != '\n': + nextval, s = parse_spaces(iter, nextval) + nextval, tok = parse_any_tok(iter, nextval) + if s == ':': + tokens[-1] += ':' + tok + else: + tokens.append(tok) + line_n += 1 + + if len(tokens) == 2 and tokens[-1] == "<<>>": + tokens.append('0') + return (next(iter, None), cls.new(line_n, tokens)) + + @classmethod + def parse_nodes(cls, char_iter): + nextval = next(char_iter, None) + while nextval: + nextval, node = cls.parse_line(char_iter, nextval) + yield node + +class VarDeclNode(AstNode): + def __init__(self, n, indent, kind, addr, begin, end, name, type, *whatev): + super(VarDeclNode, self).__init__(n, indent, kind, addr, *whatev) + self.begin = begin + self.end = end + self.name = name + self.type = type + +class MemberNode(AstNode): + def __init__(self, n, *fields): + super(MemberNode, self).__init__(n, *fields) + for f in fields: + if f.startswith(".") or f.startswith("->"): + self.member = f + +class FuncDeclNode(AstNode): + def __init__(self, n, indent, kind, addr, begin, paren, *remainder): + type = remainder[-1] + name = remainder[-2] + super(FuncDeclNode, self).__init__(n, indent, kind, addr, *remainder[:-2]) + self.type = type + self.name = name + +class ArrayFillerNode(object): + def __init__(self, cls, n, *args): + payload = list(args) + del payload[1] + self.inner = cls.new(n, payload) + + def __getattr__(self, attr): + return getattr(self.inner, attr) + +def main(): + all_names = set() + ptr_to_name = {} + + def name_for_ptr(template, ptr): + name = template + i = 0 + while name in all_names: + i += 1 + name = "%s_%i" % (template, i) + all_names.add(name) + ptr_to_name[ptr] = name + return name + + def binds_ove(node): + if node.kind == "MaterializeSequenceExpr": + return node.remainder[-1] == "" + return node.kind == "BoundsCheckExpr" + + stack = [] + skip_to_depth = None + is_next_line = False + for node in AstNode.parse_nodes(char_iter(sys.stdin)): + while stack and stack[-1].depth >= node.depth: + del stack[-1] + stack.append(node) + + may_not_skip_ove = False + + if skip_to_depth is not None: + if node.depth > skip_to_depth: + if node.kind == "OpaqueValueExpr" and not node.addr in ptr_to_name: + may_not_skip_ove = True + else: + is_next_line = False + continue + if not may_not_skip_ove: + skip_to_depth = None + if 'implicit' in node.remainder: + skip_to_depth = node.depth + is_next_line = False + continue + + if node.kind == "<<>>": + is_next_line = False + continue + + line_n_s = " " + str(line_n) if DEBUG else "" + if is_next_line: + sys.stdout.write("//" + line_n_s + " CHECK-NEXT: {{^}}") + else: + sys.stdout.write("//" + line_n_s + " CHECK: {{^}}") + is_next_line = True + sys.stdout.write(node.indent) + if isinstance(node, ArrayFillerNode): + sys.stdout.write("array_filler: ") + node = node.inner + sys.stdout.write(node.kind) + + # what else should we print for this node? + if isinstance(node, VarDeclNode): + name = name_for_ptr("var_" + node.name, node.addr) + sys.stdout.write(" [[%s:0x[^ ]+]]" % name) + + if isinstance(node, MemberNode): + sys.stdout.write(" {{.+}} %s" % node.member) + + if isinstance(node, FuncDeclNode): + name = name_for_ptr("func_" + node.name, node.addr) + sys.stdout.write(" [[%s:0x[^ ]+]]" % name) + sys.stdout.write(" {{.+}} %s" % node.name) + + if node.kind == "MaterializeSequenceExpr": + sys.stdout.write(" {{.+}} %s" % node.remainder[-1]) + + if node.kind == "DeclRefExpr": + sys.stdout.write(" {{.+}}") + for v in node.remainder: + if v.startswith("0x"): + p = int(v, 0) + if p in ptr_to_name: + sys.stdout.write(" [[%s]]" % ptr_to_name[p]) + + if node.kind == "CompoundAssignOperator": + sys.stdout.write(" {{.+}} %s %s" % node.remainder[-4:-2]) + elif node.kind.endswith("CastExpr") or node.kind.endswith("Operator"): + sys.stdout.write(" {{.+}} %s %s" % node.remainder[-2:]) + + if node.kind in ("IntegerLiteral", "BoundsSafetyPointerPromotionExpr", "GetBoundExpr", "LabelStmt"): + sys.stdout.write(" {{.+}}") + sys.stdout.write(" " + node.remainder[-1]) + + if node.kind == "BoundsCheckExpr": + sys.stdout.write(" {{.+}} %s" % node.remainder[-1]) + + if node.kind == "PredefinedBoundsCheckExpr": + sys.stdout.write(" {{.+}} %s %s" % node.remainder[-2:]) + + if node.kind == "OpaqueValueExpr": + if node.addr in ptr_to_name: + sys.stdout.write(" [[%s]]" % ptr_to_name[node.addr]) + else: + name = name_for_ptr("ove", node.addr) + sys.stdout.write(" [[%s:0x[^ ]+]]" % name) + if not binds_ove(stack[-2]): + sys.stdout.write(" {{.*}} " + node.remainder[-1]) + if not may_not_skip_ove: + skip_to_depth = node.depth + + sys.stdout.write("\n") + +if __name__ == "__main__": + sys.exit(main()) diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake index 6ccb934aef436..9b39550118c49 100644 --- a/cmake/Modules/LLVMVersion.cmake +++ b/cmake/Modules/LLVMVersion.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 1) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 4) + set(LLVM_VERSION_PATCH 5) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp index b9b1f496df7c9..be3b3bd94e2a5 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp @@ -160,33 +160,56 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset)); } +# if SANITIZER_LINUX +// Deletes the specified signal from newset, if it is not present in oldset +// Equivalently: newset[signum] = newset[signum] & oldset[signum] +static void KeepUnblocked(__sanitizer_sigset_t &newset, + __sanitizer_sigset_t &oldset, int signum) { + // FIXME: https://github.com/google/sanitizers/issues/1816 + if (SANITIZER_ANDROID || !internal_sigismember(&oldset, signum)) + internal_sigdelset(&newset, signum); +} +# endif + // Block asynchronous signals void BlockSignals(__sanitizer_sigset_t *oldset) { - __sanitizer_sigset_t set; - internal_sigfillset(&set); -# if SANITIZER_LINUX && !SANITIZER_ANDROID + __sanitizer_sigset_t newset; + internal_sigfillset(&newset); + +# if SANITIZER_LINUX + __sanitizer_sigset_t currentset; + +# if !SANITIZER_ANDROID + // FIXME: https://github.com/google/sanitizers/issues/1816 + SetSigProcMask(NULL, ¤tset); + // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked // on any thread, setuid call hangs. // See test/sanitizer_common/TestCases/Linux/setuid.c. - internal_sigdelset(&set, 33); -# endif -# if SANITIZER_LINUX + KeepUnblocked(newset, currentset, 33); +# endif // !SANITIZER_ANDROID + // Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls. // If this signal is blocked, such calls cannot be handled and the process may // hang. - internal_sigdelset(&set, 31); + KeepUnblocked(newset, currentset, 31); +# if !SANITIZER_ANDROID // Don't block synchronous signals - internal_sigdelset(&set, SIGSEGV); - internal_sigdelset(&set, SIGBUS); - internal_sigdelset(&set, SIGILL); - internal_sigdelset(&set, SIGTRAP); - internal_sigdelset(&set, SIGABRT); - internal_sigdelset(&set, SIGFPE); - internal_sigdelset(&set, SIGPIPE); -# endif + // but also don't unblock signals that the user had deliberately blocked. + // FIXME: https://github.com/google/sanitizers/issues/1816 + KeepUnblocked(newset, currentset, SIGSEGV); + KeepUnblocked(newset, currentset, SIGBUS); + KeepUnblocked(newset, currentset, SIGILL); + KeepUnblocked(newset, currentset, SIGTRAP); + KeepUnblocked(newset, currentset, SIGABRT); + KeepUnblocked(newset, currentset, SIGFPE); + KeepUnblocked(newset, currentset, SIGPIPE); +# endif //! SANITIZER_ANDROID + +# endif // SANITIZER_LINUX - SetSigProcMask(&set, oldset); + SetSigProcMask(&newset, oldset); } ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) { diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt index 2b4c15125263a..fef8bb772e0e0 100644 --- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt @@ -15,6 +15,7 @@ set(SANITIZER_UNITTESTS sanitizer_array_ref_test.cpp sanitizer_atomic_test.cpp sanitizer_bitvector_test.cpp + sanitizer_block_signals.cpp sanitizer_bvgraph_test.cpp sanitizer_chained_origin_depot_test.cpp sanitizer_common_test.cpp diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp new file mode 100644 index 0000000000000..b43648a8aef23 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp @@ -0,0 +1,76 @@ +//===-- sanitizer_block_signals.cpp ---------------------------------------===// +// +// 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 is a part of sanitizer_common unit tests. +// +//===----------------------------------------------------------------------===// +#include +#include + +#include "gtest/gtest.h" +#include "sanitizer_common/sanitizer_linux.h" + +namespace __sanitizer { + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +volatile int received_sig = -1; + +void signal_handler(int signum) { received_sig = signum; } + +TEST(SanitizerCommon, NoBlockSignals) { + // No signals blocked + signal(SIGUSR1, signal_handler); + raise(SIGUSR1); + EXPECT_EQ(received_sig, SIGUSR1); + + received_sig = -1; + signal(SIGPIPE, signal_handler); + raise(SIGPIPE); + EXPECT_EQ(received_sig, SIGPIPE); +} + +TEST(SanitizerCommon, BlockSignalsPlain) { + // ScopedBlockSignals; SIGUSR1 should be blocked but not SIGPIPE + { + __sanitizer_sigset_t sigset = {}; + ScopedBlockSignals block(&sigset); + + received_sig = -1; + signal(SIGUSR1, signal_handler); + raise(SIGUSR1); + EXPECT_EQ(received_sig, -1); + + received_sig = -1; + signal(SIGPIPE, signal_handler); + raise(SIGPIPE); + EXPECT_EQ(received_sig, SIGPIPE); + } + EXPECT_EQ(received_sig, SIGUSR1); +} + +TEST(SanitizerCommon, BlockSignalsExceptPipe) { + // Manually block SIGPIPE; ScopedBlockSignals should not unblock this + sigset_t block_sigset; + sigemptyset(&block_sigset); + sigaddset(&block_sigset, SIGPIPE); + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + { + __sanitizer_sigset_t sigset = {}; + ScopedBlockSignals block(&sigset); + + received_sig = -1; + signal(SIGPIPE, signal_handler); + raise(SIGPIPE); + EXPECT_EQ(received_sig, -1); + } + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + EXPECT_EQ(received_sig, SIGPIPE); +} +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +} // namespace __sanitizer diff --git a/libcxx/include/__config b/libcxx/include/__config index a929db5d0f2d1..33e0043136fee 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -27,7 +27,7 @@ // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. -# define _LIBCPP_VERSION 190104 +# define _LIBCPP_VERSION 190105 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt index e48fcb35aba3b..42299064fd8f7 100644 --- a/lldb/bindings/python/CMakeLists.txt +++ b/lldb/bindings/python/CMakeLists.txt @@ -198,6 +198,24 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar COMMENT "Copying Python DLL to LLDB binaries directory.") endif() + # Since Python3.8 the Windows runtime loads dependent DLLs only from the directory of the binary + # itself (and not Path). Windows has no RPATHs, so we must copy all DLLs that we depend on into + # the Python package. + if (WIN32) + # TARGET_RUNTIME_DLLS is supported in CMake 3.21+ + if ("${CMAKE_VERSION}" VERSION_LESS "3.21.0") + if (LLDB_INCLUDE_TESTS) + message(SEND_ERROR + "Your CMake version is ${CMAKE_VERSION}. In order to run LLDB tests " + "on Windows please upgrade to 3.21.0 at least (or disable tests with " + "LLDB_INCLUDE_TESTS=Off)") + endif() + else() + add_custom_command(TARGET ${swig_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy -t ${lldb_python_target_dir} $ + COMMAND_EXPAND_LISTS) + endif() + endif() endfunction() diff --git a/lldb/docs/index.rst b/lldb/docs/index.rst index d9b8e589eb2ac..73bd9d742baef 100644 --- a/lldb/docs/index.rst +++ b/lldb/docs/index.rst @@ -157,6 +157,7 @@ interesting areas to contribute to lldb. resources/fuzzing resources/sbapi resources/dataformatters + resources/formatterbytecode resources/extensions resources/lldbgdbremote resources/lldbplatformpackets diff --git a/lldb/docs/resources/formatterbytecode.rst b/lldb/docs/resources/formatterbytecode.rst new file mode 100644 index 0000000000000..a4fd2bbe804b2 --- /dev/null +++ b/lldb/docs/resources/formatterbytecode.rst @@ -0,0 +1,238 @@ +Formatter Bytecode +================== + +Background +---------- + +LLDB provides rich customization options to display data types (see :doc:`/use/variable/`). To use custom data formatters, developers need to edit the global `~/.lldbinit` file to make sure they are found and loaded. In addition to this rather manual workflow, developers or library authors can ship ship data formatters with their code in a format that allows LLDB automatically find them and run them securely. + +An end-to-end example of such a workflow is the Swift `DebugDescription` macro (see https://www.swift.org/blog/announcing-swift-6/#debugging ) that translates Swift string interpolation into LLDB summary strings, and puts them into a `.lldbsummaries` section, where LLDB can find them. + +This document describes a minimal bytecode tailored to running LLDB formatters. It defines a human-readable assembler representation for the language, an efficient binary encoding, a virtual machine for evaluating it, and format for embedding formatters into binary containers. + +Goals +~~~~~ + +Provide an efficient and secure encoding for data formatters that can be used as a compilation target from user-friendly representations (such as DIL, Swift DebugDescription, or NatVis). + +Non-goals +~~~~~~~~~ + +While humans could write the assembler syntax, making it user-friendly is not a goal. It is meant to be used as a compilation target for higher-level, language-specific affordances. + +Design of the virtual machine +----------------------------- + +The LLDB formatter virtual machine uses a stack-based bytecode, comparable with DWARF expressions, but with higher-level data types and functions. + +The virtual machine has two stacks, a data and a control stack. The control stack is kept separate to make it easier to reason about the security aspects of the virtual machine. + +Data types +~~~~~~~~~~ + +All objects on the data stack must have one of the following data types. These data types are "host" data types, in LLDB parlance. + +* *String* (UTF-8) +* *Int* (64 bit) +* *UInt* (64 bit) +* *Object* (Basically an `SBValue`) +* *Type* (Basically an `SBType`) +* *Selector* (One of the predefine functions) + +*Object* and *Type* are opaque, they can only be used as a parameters of `call`. + +Instruction set +--------------- + +Stack operations +~~~~~~~~~~~~~~~~ + +These instructions manipulate the data stack directly. + +======== ========== =========================== + Opcode Mnemonic Stack effect +-------- ---------- --------------------------- + 0x00 `dup` `(x -> x x)` + 0x01 `drop` `(x y -> x)` + 0x02 `pick` `(x ... UInt -> x ... x)` + 0x03 `over` `(x y -> x y x)` + 0x04 `swap` `(x y -> y x)` + 0x05 `rot` `(x y z -> z x y)` +======= ========== =========================== + +Control flow +~~~~~~~~~~~~ + +These manipulate the control stack and program counter. Both `if` and `ifelse` expect a `UInt` at the top of the data stack to represent the condition. + +======== ========== ============================================================ + Opcode Mnemonic Description +-------- ---------- ------------------------------------------------------------ + 0x10 `{` push a code block address onto the control stack + -- `}` (technically not an opcode) syntax for end of code block + 0x11 `if` `(UInt -> )` pop a block from the control stack, + if the top of the data stack is nonzero, execute it + 0x12 `ifelse` `(UInt -> )` pop two blocks from the control stack, if + the top of the data stack is nonzero, execute the first, + otherwise the second. +======== ========== ============================================================ + +Literals for basic types +~~~~~~~~~~~~~~~~~~~~~~~~ + +======== =========== ============================================================ + Opcode Mnemonic Description +-------- ----------- ------------------------------------------------------------ + 0x20 `123u` `( -> UInt)` push an unsigned 64-bit host integer + 0x21 `123` `( -> Int)` push a signed 64-bit host integer + 0x22 `"abc"` `( -> String)` push a UTF-8 host string + 0x23 `@strlen` `( -> Selector)` push one of the predefined function + selectors. See `call`. +======== =========== ============================================================ + +Conversion operations +~~~~~~~~~~~~~~~~~~~~~ + +======== =========== ================================================================ + Opcode Mnemonic Description +-------- ----------- ---------------------------------------------------------------- + 0x2a `as_int` `( UInt -> Int)` reinterpret a UInt as an Int + 0x2b `as_uint` `( Int -> UInt)` reinterpret an Int as a UInt + 0x2c `is_null` `( Object -> UInt )` check an object for null `(object ? 0 : 1)` +======== =========== ================================================================ + + +Arithmetic, logic, and comparison operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All of these operations are only defined for `Int` and `UInt` and both operands need to be of the same type. The `>>` operator is an arithmetic shift if the parameters are of type `Int`, otherwise it's a logical shift to the right. + +======== ========== =========================== + Opcode Mnemonic Stack effect +-------- ---------- --------------------------- + 0x30 `+` `(x y -> [x+y])` + 0x31 `-` etc ... + 0x32 `*` + 0x33 `/` + 0x34 `%` + 0x35 `<<` + 0x36 `>>` + 0x40 `~` + 0x41 `|` + 0x42 `^` + 0x50 `=` + 0x51 `!=` + 0x52 `<` + 0x53 `>` + 0x54 `=<` + 0x55 `>=` +======== ========== =========================== + +Function calls +~~~~~~~~~~~~~~ + +For security reasons the list of functions callable with `call` is predefined. The supported functions are either existing methods on `SBValue`, or string formatting operations. + +======== ========== ============================================ + Opcode Mnemonic Stack effect +-------- ---------- -------------------------------------------- + 0x60 `call` `(Object argN ... arg0 Selector -> retval)` +======== ========== ============================================ + +Method is one of a predefined set of *Selectors*. + +==== ============================ =================================================== ================================== +Sel. Mnemonic Stack Effect Description +---- ---------------------------- --------------------------------------------------- ---------------------------------- +0x00 `summary` `(Object @summary -> String)` `SBValue::GetSummary` +0x01 `type_summary` `(Object @type_summary -> String)` `SBValue::GetTypeSummary` +0x10 `get_num_children` `(Object @get_num_children -> UInt)` `SBValue::GetNumChildren` +0x11 `get_child_at_index` `(Object UInt @get_child_at_index -> Object)` `SBValue::GetChildAtIndex` +0x12 `get_child_with_name` `(Object String @get_child_with_name -> Object)` `SBValue::GetChildAtIndex` +0x13 `get_child_index` `(Object String @get_child_index -> UInt)` `SBValue::GetChildIndex` +0x15 `get_type` `(Object @get_type -> Type)` `SBValue::GetType` +0x16 `get_template_argument_type` `(Object UInt @get_template_argument_type -> Type)` `SBValue::GetTemplateArgumentType` +0x17 `cast` `(Object Type @cast -> Object)` `SBValue::Cast` +0x20 `get_value` `(Object @get_value -> Object)` `SBValue::GetValue` +0x21 `get_value_as_unsigned` `(Object @get_value_as_unsigned -> UInt)` `SBValue::GetValueAsUnsigned` +0x22 `get_value_as_signed` `(Object @get_value_as_signed -> Int)` `SBValue::GetValueAsSigned` +0x23 `get_value_as_address` `(Object @get_value_as_address -> UInt)` `SBValue::GetValueAsAddress` +0x40 `read_memory_byte` `(UInt @read_memory_byte -> UInt)` `Target::ReadMemory` +0x41 `read_memory_uint32` `(UInt @read_memory_uint32 -> UInt)` `Target::ReadMemory` +0x42 `read_memory_int32` `(UInt @read_memory_int32 -> Int)` `Target::ReadMemory` +0x43 `read_memory_uint64` `(UInt @read_memory_uint64 -> UInt)` `Target::ReadMemory` +0x44 `read_memory_int64` `(UInt @read_memory_int64 -> Int)` `Target::ReadMemory` +0x45 `read_memory_address` `(UInt @read_memory_uint64 -> UInt)` `Target::ReadMemory` +0x46 `read_memory` `(UInt Type @read_memory -> Object)` `Target::ReadMemory` +0x50 `fmt` `(String arg0 ... @fmt -> String)` `llvm::format` +0x51 `sprintf` `(String arg0 ... sprintf -> String)` `sprintf` +0x52 `strlen` `(String strlen -> String)` `strlen in bytes` +==== ============================ =================================================== ================================== + +Byte Code +~~~~~~~~~ + +Most instructions are just a single byte opcode. The only exceptions are the literals: + +* *String*: Length in bytes encoded as ULEB128, followed length bytes +* *Int*: LEB128 +* *UInt*: ULEB128 +* *Selector*: ULEB128 + +Embedding +~~~~~~~~~ + +Expression programs are embedded into an `.lldbformatters` section (an evolution of the Swift `.lldbsummaries` section) that is a dictionary of type names/regexes and descriptions. It consists of a list of records. Each record starts with the following header: + +* Version number (ULEB128) +* Remaining size of the record (minus the header) (ULEB128) + +The version number is increased whenever an incompatible change is made. Adding new opcodes or selectors is not an incompatible change since consumers can unambiguously detect this and report an error. + +Space between two records may be padded with NULL bytes. + +In version 1, a record consists of a dictionary key, which is a type name or regex. + +* Length of the key in bytes (ULEB128) +* The key (UTF-8) + +A regex has to start with `^`, which is part of the regular expression. + +After this comes a flag bitfield, which is a ULEB-encoded `lldb::TypeOptions` bitfield. + +* Flags (ULEB128) + + +This is followed by one or more dictionary values that immediately follow each other and entirely fill out the record size from the header. Each expression program has the following layout: + +* Function signature (1 byte) +* Length of the program (ULEB128) +* The program bytecode + +The possible function signatures are: + +========= ====================== ========================== +Signature Mnemonic Stack Effect +--------- ---------------------- -------------------------- + 0x00 `@summary` `(Object -> String)` + 0x01 `@init` `(Object -> Object+)` + 0x02 `@get_num_children` `(Object+ -> UInt)` + 0x03 `@get_child_index` `(Object+ String -> UInt)` + 0x04 `@get_child_at_index` `(Object+ UInt -> Object)` + 0x05 `@get_value` `(Object+ -> String)` +========= ====================== ========================== + +If not specified, the init function defaults to an empty function that just passes the Object along. Its results may be cached and allow common prep work to be done for an Object that can be reused by subsequent calls to the other methods. This way subsequent calls to `@get_child_at_index` can avoid recomputing shared information, for example. + +While it is more efficient to store multiple programs per type key, this is not a requirement. LLDB will merge all entries. If there are conflicts the result is undefined. + +Execution model +~~~~~~~~~~~~~~~ + +Execution begins at the first byte in the program. The program counter of the virtual machine starts at offset 0 of the bytecode and may never move outside the range of the program as defined in the header. The data stack starts with one Object or the result of the `@init` function (`Object+` in the table above). + +Error handling +~~~~~~~~~~~~~~ + +In version 1 errors are unrecoverable, the entire expression will fail if any kind of error is encountered. + diff --git a/lldb/examples/python/crashlog.py b/lldb/examples/python/crashlog.py index 368437ed63e46..ab8c2fcaf034b 100755 --- a/lldb/examples/python/crashlog.py +++ b/lldb/examples/python/crashlog.py @@ -1512,7 +1512,7 @@ def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result): arch = crashlog.process_arch if not arch: raise InteractiveCrashLogException( - "couldn't create find the architecture to create the target" + "couldn't find the architecture to create the target" ) target = debugger.CreateTargetWithFileAndArch(None, arch) # 4. Fail diff --git a/lldb/examples/python/formatter_bytecode.py b/lldb/examples/python/formatter_bytecode.py new file mode 100644 index 0000000000000..ccd0c68a75483 --- /dev/null +++ b/lldb/examples/python/formatter_bytecode.py @@ -0,0 +1,524 @@ +""" +Specification, compiler, disassembler, and interpreter +for LLDB dataformatter bytecode. + +See https://lldb.llvm.org/resources/formatterbytecode.html for more details. +""" + +from __future__ import annotations + +# Types +type_String = 1 +type_Int = 2 +type_UInt = 3 +type_Object = 4 +type_Type = 5 + +# Opcodes +opcode = dict() + + +def define_opcode(n, mnemonic, name): + globals()["op_" + name] = n + if mnemonic: + opcode[mnemonic] = n + opcode[n] = mnemonic + + +define_opcode(1, "dup", "dup") +define_opcode(2, "drop", "drop") +define_opcode(3, "pick", "pick") +define_opcode(4, "over", "over") +define_opcode(5, "swap", "swap") +define_opcode(6, "rot", "rot") + +define_opcode(0x10, "{", "begin") +define_opcode(0x11, "if", "if") +define_opcode(0x12, "ifelse", "ifelse") + +define_opcode(0x20, None, "lit_uint") +define_opcode(0x21, None, "lit_int") +define_opcode(0x22, None, "lit_string") +define_opcode(0x23, None, "lit_selector") + +define_opcode(0x2A, "as_int", "as_int") +define_opcode(0x2B, "as_uint", "as_uint") +define_opcode(0x2C, "is_null", "is_null") + +define_opcode(0x30, "+", "plus") +define_opcode(0x31, "-", "minus") +define_opcode(0x32, "*", "mul") +define_opcode(0x33, "/", "div") +define_opcode(0x34, "%", "mod") +define_opcode(0x35, "<<", "shl") +define_opcode(0x36, ">>", "shr") + +define_opcode(0x40, "&", "and") +define_opcode(0x41, "|", "or") +define_opcode(0x42, "^", "xor") +define_opcode(0x43, "~", "not") + +define_opcode(0x50, "=", "eq") +define_opcode(0x51, "!=", "neq") +define_opcode(0x52, "<", "lt") +define_opcode(0x53, ">", "gt") +define_opcode(0x54, "=<", "le") +define_opcode(0x55, ">=", "ge") + +define_opcode(0x60, "call", "call") + +# Function signatures +sig_summary = 0 +sig_init = 1 +sig_get_num_children = 2 +sig_get_child_index = 3 +sig_get_child_at_index = 4 + +# Selectors +selector = dict() + + +def define_selector(n, name): + globals()["sel_" + name] = n + selector["@" + name] = n + selector[n] = "@" + name + + +define_selector(0, "summary") +define_selector(1, "type_summary") + +define_selector(0x10, "get_num_children") +define_selector(0x11, "get_child_at_index") +define_selector(0x12, "get_child_with_name") +define_selector(0x13, "get_child_index") +define_selector(0x15, "get_type") +define_selector(0x16, "get_template_argument_type") +define_selector(0x17, "cast") +define_selector(0x20, "get_value") +define_selector(0x21, "get_value_as_unsigned") +define_selector(0x22, "get_value_as_signed") +define_selector(0x23, "get_value_as_address") + +define_selector(0x40, "read_memory_byte") +define_selector(0x41, "read_memory_uint32") +define_selector(0x42, "read_memory_int32") +define_selector(0x43, "read_memory_unsigned") +define_selector(0x44, "read_memory_signed") +define_selector(0x45, "read_memory_address") +define_selector(0x46, "read_memory") + +define_selector(0x50, "fmt") +define_selector(0x51, "sprintf") +define_selector(0x52, "strlen") + + +################################################################################ +# Compiler. +################################################################################ + + +def compile(assembler: str) -> bytearray: + """Compile assembler into bytecode""" + # This is a stack of all in-flight/unterminated blocks. + bytecode = [bytearray()] + + def emit(byte): + bytecode[-1].append(byte) + + tokens = list(assembler.split(" ")) + tokens.reverse() + while tokens: + tok = tokens.pop() + if tok == "": + pass + elif tok == "{": + bytecode.append(bytearray()) + elif tok == "}": + block = bytecode.pop() + emit(op_begin) + emit(len(block)) # FIXME: uleb + bytecode[-1].extend(block) + elif tok[0].isdigit(): + if tok[-1] == "u": + emit(op_lit_uint) + emit(int(tok[:-1])) # FIXME + else: + emit(op_lit_int) + emit(int(tok)) # FIXME + elif tok[0] == "@": + emit(op_lit_selector) + emit(selector[tok]) + elif tok[0] == '"': + s = bytearray() + done = False + chrs = tok[1:] + while not done: + quoted = False + for c in chrs: + if quoted: + s.append(ord(c)) # FIXME + quoted = False + elif c == "\\": + quoted = True + elif c == '"': + done = True + break + # FIXME assert this is last in token + else: + s.append(ord(c)) + if not done: + s.append(ord(" ")) + chrs = tokens.pop() + + emit(op_lit_string) + emit(len(s)) + bytecode[-1].extend(s) + else: + emit(opcode[tok]) + assert len(bytecode) == 1 # unterminated { + return bytecode[0] + + +################################################################################ +# Disassembler. +################################################################################ + + +def disassemble(bytecode: bytearray) -> (str, int): + """Disassemble bytecode into (assembler, token starts)""" + asm = "" + all_bytes = list(bytecode) + all_bytes.reverse() + blocks = [] + tokens = [0] + + def next_byte(): + """Fetch the next byte in the bytecode and keep track of all + in-flight blocks""" + for i in range(len(blocks)): + blocks[i] -= 1 + tokens.append(len(asm)) + return all_bytes.pop() + + while all_bytes: + b = next_byte() + if b == op_begin: + asm += "{" + length = next_byte() + blocks.append(length) + elif b == op_lit_uint: + b = next_byte() + asm += str(b) # FIXME uleb + asm += "u" + elif b == op_lit_int: + b = next_byte() + asm += str(b) + elif b == op_lit_selector: + b = next_byte() + asm += selector[b] + elif b == op_lit_string: + length = next_byte() + s = "'" + while length: + s += chr(next_byte()) + length -= 1 + asm += '"' + repr(s)[2:] + else: + asm += opcode[b] + + while blocks and blocks[-1] == 0: + asm += " }" + blocks.pop() + + if all_bytes: + asm += " " + + if blocks: + asm += "ERROR" + return asm, tokens + + +################################################################################ +# Interpreter. +################################################################################ + + +def count_fmt_params(fmt: str) -> int: + """Count the number of parameters in a format string""" + from string import Formatter + + f = Formatter() + n = 0 + for _, name, _, _ in f.parse(fmt): + if name > n: + n = name + return n + + +def interpret(bytecode: bytearray, control: list, data: list, tracing: bool = False): + """Interpret bytecode""" + frame = [] + frame.append((0, len(bytecode))) + + def trace(): + """print a trace of the execution for debugging purposes""" + + def fmt(d): + if isinstance(d, int): + return str(d) + if isinstance(d, str): + return d + return repr(type(d)) + + pc, end = frame[-1] + asm, tokens = disassemble(bytecode) + print( + "=== frame = {1}, data = {2}, opcode = {0}".format( + opcode[b], frame, [fmt(d) for d in data] + ) + ) + print(asm) + print(" " * (tokens[pc]) + "^") + + def next_byte(): + """Fetch the next byte and update the PC""" + pc, end = frame[-1] + assert pc < len(bytecode) + b = bytecode[pc] + frame[-1] = pc + 1, end + # At the end of a block? + while pc >= end: + frame.pop() + if not frame: + return None + pc, end = frame[-1] + if pc >= end: + return None + b = bytecode[pc] + frame[-1] = pc + 1, end + return b + + while frame[-1][0] < len(bytecode): + b = next_byte() + if b == None: + break + if tracing: + trace() + # Data stack manipulation. + if b == op_dup: + data.append(data[-1]) + elif b == op_drop: + data.pop() + elif b == op_pick: + data.append(data[data.pop()]) + elif b == op_over: + data.append(data[-2]) + elif b == op_swap: + x = data.pop() + y = data.pop() + data.append(x) + data.append(y) + elif b == op_rot: + z = data.pop() + y = data.pop() + x = data.pop() + data.append(z) + data.append(x) + data.append(y) + + # Control stack manipulation. + elif b == op_begin: + length = next_byte() + pc, end = frame[-1] + control.append((pc, pc + length)) + frame[-1] = pc + length, end + elif b == op_if: + if data.pop(): + frame.append(control.pop()) + elif b == op_ifelse: + if data.pop(): + control.pop() + frame.append(control.pop()) + else: + frame.append(control.pop()) + control.pop() + + # Literals. + elif b == op_lit_uint: + b = next_byte() # FIXME uleb + data.append(int(b)) + elif b == op_lit_int: + b = next_byte() # FIXME uleb + data.append(int(b)) + elif b == op_lit_selector: + b = next_byte() + data.append(b) + elif b == op_lit_string: + length = next_byte() + s = "" + while length: + s += chr(next_byte()) + length -= 1 + data.append(s) + + elif b == op_as_uint: + pass + elif b == op_as_int: + pass + elif b == op_is_null: + data.append(1 if data.pop() == None else 0) + + # Arithmetic, logic, etc. + elif b == op_plus: + data.append(data.pop() + data.pop()) + elif b == op_minus: + data.append(-data.pop() + data.pop()) + elif b == op_mul: + data.append(data.pop() * data.pop()) + elif b == op_div: + y = data.pop() + data.append(data.pop() / y) + elif b == op_mod: + y = data.pop() + data.append(data.pop() % y) + elif b == op_shl: + y = data.pop() + data.append(data.pop() << y) + elif b == op_shr: + y = data.pop() + data.append(data.pop() >> y) + elif b == op_and: + data.append(data.pop() & data.pop()) + elif b == op_or: + data.append(data.pop() | data.pop()) + elif b == op_xor: + data.append(data.pop() ^ data.pop()) + elif b == op_not: + data.append(not data.pop()) + elif b == op_eq: + data.append(data.pop() == data.pop()) + elif b == op_neq: + data.append(data.pop() != data.pop()) + elif b == op_lt: + data.append(data.pop() > data.pop()) + elif b == op_gt: + data.append(data.pop() < data.pop()) + elif b == op_le: + data.append(data.pop() >= data.pop()) + elif b == op_ge: + data.append(data.pop() <= data.pop()) + + # Function calls. + elif b == op_call: + sel = data.pop() + if sel == sel_summary: + data.append(data.pop().GetSummary()) + elif sel == sel_get_num_children: + data.append(data.pop().GetNumChildren()) + elif sel == sel_get_child_at_index: + index = data.pop() + valobj = data.pop() + data.append(valobj.GetChildAtIndex(index)) + elif sel == sel_get_child_with_name: + name = data.pop() + valobj = data.pop() + data.append(valobj.GetChildMemberWithName(name)) + elif sel == sel_get_child_index: + name = data.pop() + valobj = data.pop() + data.append(valobj.GetIndexOfChildWithName(name)) + elif sel == sel_get_type: + data.append(data.pop().GetType()) + elif sel == sel_get_template_argument_type: + n = data.pop() + valobj = data.pop() + data.append(valobj.GetTemplateArgumentType(n)) + elif sel == sel_get_value: + data.append(data.pop().GetValue()) + elif sel == sel_get_value_as_unsigned: + data.append(data.pop().GetValueAsUnsigned()) + elif sel == sel_get_value_as_signed: + data.append(data.pop().GetValueAsSigned()) + elif sel == sel_get_value_as_address: + data.append(data.pop().GetValueAsAddress()) + elif sel == sel_cast: + sbtype = data.pop() + valobj = data.pop() + data.append(valobj.Cast(sbtype)) + elif sel == sel_strlen: + s = data.pop() + data.append(len(s) if s else 0) + elif sel == sel_fmt: + fmt = data.pop() + n = count_fmt_params(fmt) + args = [] + for i in range(n): + args.append(data.pop()) + data.append(fmt.format(*args)) + else: + print("not implemented: " + selector[sel]) + assert False + pass + return data[-1] + + +if __name__ == "__main__": + # Work around the fact that one of the local files is called + # types.py, which breaks some versions of python. + import os, sys + + path = os.path.abspath(os.path.dirname(__file__)) + sys.path.remove(path) + import argparse + + parser = argparse.ArgumentParser( + description=""" + Compiler, disassembler, and interpreter for LLDB dataformatter bytecode. + See https://lldb.llvm.org/resources/formatterbytecode.html for more details. + """ + ) + parser.add_argument( + "-c", "--compile", type=str, help="compile assembler into bytecode" + ) + parser.add_argument("-d", "--disassemble", type=str, help="disassemble bytecode") + parser.add_argument("-t", "--test", action="store_true", help="run unit tests") + args = parser.parse_args() + if args.compile: + print(compile(str(args.compile)).hex()) + + if args.disassemble: + print(disassemble(bytearray.fromhex(str(args.disassemble)))) + + ############################################################################ + # Tests. + ############################################################################ + if args.test: + import unittest + + class TestCompiler(unittest.TestCase): + def test(self): + self.assertEqual(compile("1u dup").hex(), "200101") + self.assertEqual(compile('"1u dup"').hex(), "2206317520647570") + self.assertEqual(compile("16 < { dup } if").hex(), "21105210010111") + self.assertEqual(compile('{ { " } " } }').hex(), "100710052203207d20") + + def roundtrip(asm): + self.assertEqual(disassemble(compile(asm))[0], asm) + + roundtrip("1u dup") + roundtrip('1u dup "1u dup"') + roundtrip("16 < { dup } if") + roundtrip('{ { " } " } }') + + self.assertEqual(interpret(compile("1 1 +"), [], []), 2) + self.assertEqual(interpret(compile("2 1 1 + *"), [], []), 4) + self.assertEqual( + interpret(compile('2 1 > { "yes" } { "no" } ifelse'), [], []), "yes" + ) + + import sys + + sys.argv.pop() + path = os.path.dirname(__file__) + sys.path.remove + unittest.main() diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h index 40c4e28174613..787bd040dd15b 100644 --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -430,6 +430,11 @@ class LLDB_API SBDebugger { SBTypeSynthetic GetSyntheticForType(SBTypeNameSpecifier); + /// Clear collected statistics for targets belonging to this debugger. This + /// includes clearing symbol table and debug info parsing/index time for all + /// modules, breakpoint resolve time and target statistics. + void ResetStatistics(); + #ifndef SWIG /// Run the command interpreter. /// diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index 2cac280449084..43fe71da38321 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -101,6 +101,11 @@ class LLDB_API SBTarget { /// A SBStructuredData with the statistics collected. lldb::SBStructuredData GetStatistics(SBStatisticsOptions options); + /// Reset the statistics collected for this target. + /// This includes clearing symbol table and debug info parsing/index time for + /// all modules, breakpoint resolve time and target statistics. + void ResetStatistics(); + /// Return the platform object associated with the target. /// /// After return, the platform object should be checked for diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h index 0b6bf593be37a..f623a2e0c295b 100644 --- a/lldb/include/lldb/Breakpoint/Breakpoint.h +++ b/lldb/include/lldb/Breakpoint/Breakpoint.h @@ -588,6 +588,8 @@ class Breakpoint : public std::enable_shared_from_this, /// Get statistics associated with this breakpoint in JSON format. llvm::json::Value GetStatistics(); + void ResetStatistics(); + /// Get the time it took to resolve all locations in this breakpoint. StatsDuration::Duration GetResolveTime() const { return m_resolve_time; } diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 0b0a465a09b2e..ce1eaf08d584d 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -916,6 +916,8 @@ class Module : public std::enable_shared_from_this, /// ElapsedTime RAII object. StatsDuration &GetSymtabIndexTime() { return m_symtab_index_time; } + void ResetStatistics(); + /// \class LookupInfo Module.h "lldb/Core/Module.h" /// A class that encapsulates name lookup information. /// diff --git a/lldb/include/lldb/DataFormatters/FormatterSection.h b/lldb/include/lldb/DataFormatters/FormatterSection.h new file mode 100644 index 0000000000000..0613fba6c3a60 --- /dev/null +++ b/lldb/include/lldb/DataFormatters/FormatterSection.h @@ -0,0 +1,26 @@ +//===-- FormattersSection.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_DATAFORMATTERS_FORMATTERSECTION_H +#define LLDB_DATAFORMATTERS_FORMATTERSECTION_H + +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +namespace lldb_private { +/// Load type summaries embedded in the binary. These are type summaries +/// provided by the authors of the code. +void LoadTypeSummariesForModule(lldb::ModuleSP module_sp); + +/// Load data formatters embedded in the binary. These are formatters provided +/// by the authors of the code using LLDB formatter bytecode. +void LoadFormattersForModule(lldb::ModuleSP module_sp); + +} // namespace lldb_private + +#endif // LLDB_DATAFORMATTERS_FORMATTERSECTION_H diff --git a/lldb/include/lldb/DataFormatters/FormattersHelpers.h b/lldb/include/lldb/DataFormatters/FormattersHelpers.h index a2e8521d96651..6ec368484e150 100644 --- a/lldb/include/lldb/DataFormatters/FormattersHelpers.h +++ b/lldb/include/lldb/DataFormatters/FormattersHelpers.h @@ -1,5 +1,4 @@ -//===-- FormattersHelpers.h --------------------------------------*- C++ -//-*-===// +//===-- FormattersHelpers.h -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,6 +19,15 @@ namespace lldb_private { namespace formatters { + +/* TO_UPSTREAM(BoundsSafety) ON */ +bool FormatBoundsSafetyPointer(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &); +bool FormatBoundsSafetyAttrPointer(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &); +bool FormatBoundsSafetyDynamicRangeAttrPointer(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &); +/* TO_UPSTREAM(BoundsSafety) OFF */ + void AddFormat(TypeCategoryImpl::SharedPointer category_sp, lldb::Format format, llvm::StringRef type_name, TypeFormatImpl::Flags flags, bool regex = false); diff --git a/lldb/include/lldb/DataFormatters/TypeSummary.h b/lldb/include/lldb/DataFormatters/TypeSummary.h index a82641021dad3..f4d5563516ecb 100644 --- a/lldb/include/lldb/DataFormatters/TypeSummary.h +++ b/lldb/include/lldb/DataFormatters/TypeSummary.h @@ -22,6 +22,10 @@ #include "lldb/Utility/Status.h" #include "lldb/Utility/StructuredData.h" +namespace llvm { +class MemoryBuffer; +} + namespace lldb_private { class TypeSummaryOptions { public: @@ -44,7 +48,7 @@ class TypeSummaryOptions { class TypeSummaryImpl { public: - enum class Kind { eSummaryString, eScript, eCallback, eInternal }; + enum class Kind { eSummaryString, eScript, eBytecode, eCallback, eInternal }; virtual ~TypeSummaryImpl() = default; @@ -258,6 +262,14 @@ class TypeSummaryImpl { virtual std::string GetDescription() = 0; + /// Get the name of the Type Summary Provider, either a C++ class, a summary + /// string, or a script function name. + virtual std::string GetName() = 0; + + /// Get the name of the kind of Summary Provider, either c++, summary string, + /// script or python. + virtual std::string GetSummaryKindName(); + uint32_t &GetRevision() { return m_my_revision; } typedef std::shared_ptr SharedPointer; @@ -293,6 +305,8 @@ struct StringSummaryFormat : public TypeSummaryImpl { std::string GetDescription() override; + std::string GetName() override; + static bool classof(const TypeSummaryImpl *S) { return S->GetKind() == Kind::eSummaryString; } @@ -340,6 +354,8 @@ struct CXXFunctionSummaryFormat : public TypeSummaryImpl { return S->GetKind() == Kind::eCallback; } + std::string GetName() override; + typedef std::shared_ptr SharedPointer; private: @@ -352,6 +368,7 @@ struct CXXFunctionSummaryFormat : public TypeSummaryImpl { struct ScriptSummaryFormat : public TypeSummaryImpl { std::string m_function_name; std::string m_python_script; + std::string m_script_formatter_name; StructuredData::ObjectSP m_script_function_sp; ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags, @@ -384,6 +401,8 @@ struct ScriptSummaryFormat : public TypeSummaryImpl { std::string GetDescription() override; + std::string GetName() override; + static bool classof(const TypeSummaryImpl *S) { return S->GetKind() == Kind::eScript; } @@ -394,6 +413,23 @@ struct ScriptSummaryFormat : public TypeSummaryImpl { ScriptSummaryFormat(const ScriptSummaryFormat &) = delete; const ScriptSummaryFormat &operator=(const ScriptSummaryFormat &) = delete; }; + +/// A summary formatter that is defined in LLDB formmater bytecode. +class BytecodeSummaryFormat : public TypeSummaryImpl { + std::unique_ptr m_bytecode; + +public: + BytecodeSummaryFormat(const TypeSummaryImpl::Flags &flags, + std::unique_ptr bytecode); + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options) override; + std::string GetDescription() override; + std::string GetName() override; + static bool classof(const TypeSummaryImpl *S) { + return S->GetKind() == Kind::eBytecode; + } +}; + } // namespace lldb_private #endif // LLDB_DATAFORMATTERS_TYPESUMMARY_H diff --git a/lldb/include/lldb/Symbol/CompileUnit.h b/lldb/include/lldb/Symbol/CompileUnit.h index c20a37e328307..c5bb080d21184 100644 --- a/lldb/include/lldb/Symbol/CompileUnit.h +++ b/lldb/include/lldb/Symbol/CompileUnit.h @@ -19,11 +19,13 @@ #include "lldb/Utility/Stream.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" namespace lldb_private { + /// \class CompileUnit CompileUnit.h "lldb/Symbol/CompileUnit.h" /// A class that describes a compilation unit. /// @@ -389,10 +391,15 @@ class CompileUnit : public std::enable_shared_from_this, /// A SymbolContext list class that will get any matching /// entries appended to. /// + /// \param[in] realpath_prefixes + /// Paths that start with one of the prefixes in this list will be + /// realpath'ed to resolve any symlinks. + /// /// \see enum SymbolContext::Scope void ResolveSymbolContext(const SourceLocationSpec &src_location_spec, lldb::SymbolContextItem resolve_scope, - SymbolContextList &sc_list); + SymbolContextList &sc_list, + RealpathPrefixes *realpath_prefixes = nullptr); /// Get whether compiler optimizations were enabled for this compile unit /// diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 5d94349f23a01..7d88672a2d533 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -143,6 +143,16 @@ class CompilerType { bool IsDefined() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// If the CompilerType is a PointerType and BoundsSafety PointerAttribute + /// Indexable set returns true otherwise returns false. + bool IsBoundsSafetyIndexable() const; + + /// If the CompilerType is a PointerType and BoundsSafety PointerAttribute + /// BidiIndexable set returns true otherwise returns false. + bool IsBoundsSafetyBidiIndexable() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + bool IsFloatingPointType(uint32_t &count, bool &is_complex) const; bool IsFunctionType() const; @@ -355,6 +365,23 @@ class CompilerType { /// an invalid type. CompilerType AddVolatileModifier() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// If this is a PointerType return a new CompilerType that has the + /// BoundsSafetyPointerAttributes Indexable added to this type + /// otherwise return an invalid type. + CompilerType AddBoundsSafetyIndexableAttribute() const; + + /// If this is a PointerType return a new CompilerType that has the + /// BoundsSafetyPointerAttributes BidiIndexable added to this type + /// otherwise return an invalid type. + CompilerType AddBoundsSafetyBidiIndexableAttribute() const; + + /// If this is a PointerType return a new CompilerType that has the + /// BoundsSafetyPointerAttributes UnsafeIndexable added to this type + /// otherwise return an invalid type. + CompilerType AddBoundsSafetyUnspecifiedAttribute() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Return a new CompilerType that is the atomic type of this type. If this /// type is not valid or the type system doesn't support atomic types, this /// returns an invalid type. diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 84abfe7204f3b..260a104f1acf6 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -461,6 +461,9 @@ class SymbolFile : public PluginInterface { /// hasn't been indexed yet, or a valid duration if it has. virtual StatsDuration::Duration GetDebugInfoIndexTime() { return {}; } + /// Reset the statistics for the symbol file. + virtual void ResetStatistics() {} + /// Get the additional modules that this symbol file uses to parse debug info. /// /// Some debug info is stored in stand alone object files that are represented diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h index 8073d1816860e..7a366bfabec86 100644 --- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h +++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h @@ -182,6 +182,8 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile { lldb_private::StatsDuration::Duration GetDebugInfoParseTime() override; lldb_private::StatsDuration::Duration GetDebugInfoIndexTime() override; + void ResetStatistics() override; + uint32_t GetAbilities() override; Symtab *GetSymtab() override { return m_sym_file_impl->GetSymtab(); } diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 9df27c462cb7c..db12271b7e869 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -223,6 +223,10 @@ class TypeSystem : public PluginInterface, // with this type system. For such cases, languages can check and return // an error. virtual Status IsCompatible(); + /* TO_UPSTREAM(BoundsSafety) ON */ + virtual bool IsBoundsSafetyIndexable(lldb::opaque_compiler_type_t type) = 0; + virtual bool IsBoundsSafetyBidiIndexable(lldb::opaque_compiler_type_t type) = 0; + /* TO_UPSTREAM(BoundsSafety) OFF */ static bool SupportsLanguageStatic(lldb::LanguageType language); // Type Completion @@ -317,6 +321,12 @@ class TypeSystem : public PluginInterface, virtual CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type); + /* TO_UPSTREAM(BoundsSafety) ON */ + virtual CompilerType AddBoundsSafetyIndexableAttribute(lldb::opaque_compiler_type_t type); + virtual CompilerType AddBoundsSafetyBidiIndexableAttribute(lldb::opaque_compiler_type_t type); + virtual CompilerType AddBoundsSafetyUnspecifiedAttribute(lldb::opaque_compiler_type_t type); + /* TO_UPSTREAM(BoundsSafety) OFF */ + virtual CompilerType AddPtrAuthModifier(lldb::opaque_compiler_type_t type, uint32_t payload); diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h index 35bd7f8a66e05..ee365357fcf31 100644 --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -9,13 +9,16 @@ #ifndef LLDB_TARGET_STATISTICS_H #define LLDB_TARGET_STATISTICS_H +#include "lldb/DataFormatters/TypeSummary.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/RealpathPrefixes.h" #include "lldb/Utility/Stream.h" #include "lldb/lldb-forward.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/JSON.h" #include #include +#include #include #include #include @@ -25,6 +28,9 @@ namespace lldb_private { using StatsClock = std::chrono::high_resolution_clock; using StatsTimepoint = std::chrono::time_point; +class SummaryStatistics; +// Declaring here as there is no private forward +typedef std::shared_ptr SummaryStatisticsSP; class StatsDuration { public: @@ -35,6 +41,8 @@ class StatsDuration { } operator Duration() const { return get(); } + void reset() { value.store(0, std::memory_order_relaxed); } + StatsDuration &operator+=(Duration dur) { value.fetch_add(std::chrono::duration_cast(dur).count(), std::memory_order_relaxed); @@ -174,6 +182,85 @@ struct StatisticsOptions { std::optional m_include_transcript; }; +/// A class that represents statistics about a TypeSummaryProviders invocations +/// \note All members of this class need to be accessed in a thread safe manner +class SummaryStatistics { +public: + explicit SummaryStatistics(std::string name, std::string impl_type) + : m_total_time(), m_impl_type(std::move(impl_type)), + m_name(std::move(name)), m_count(0) {} + + std::string GetName() const { return m_name; }; + double GetTotalTime() const { return m_total_time.get().count(); } + + uint64_t GetSummaryCount() const { + return m_count.load(std::memory_order_relaxed); + } + + StatsDuration &GetDurationReference() { return m_total_time; }; + + std::string GetSummaryKindName() const { return m_impl_type; } + + llvm::json::Value ToJSON() const; + + void Reset() { m_total_time.reset(); } + + /// Basic RAII class to increment the summary count when the call is complete. + class SummaryInvocation { + public: + SummaryInvocation(SummaryStatisticsSP summary_stats) + : m_stats(summary_stats), + m_elapsed_time(summary_stats->GetDurationReference()) {} + ~SummaryInvocation() { m_stats->OnInvoked(); } + + /// Delete the copy constructor and assignment operator to prevent + /// accidental double counting. + /// @{ + SummaryInvocation(const SummaryInvocation &) = delete; + SummaryInvocation &operator=(const SummaryInvocation &) = delete; + /// @} + + private: + SummaryStatisticsSP m_stats; + ElapsedTime m_elapsed_time; + }; + +private: + void OnInvoked() noexcept { m_count.fetch_add(1, std::memory_order_relaxed); } + lldb_private::StatsDuration m_total_time; + const std::string m_impl_type; + const std::string m_name; + std::atomic m_count; +}; + +/// A class that wraps a std::map of SummaryStatistics objects behind a mutex. +class SummaryStatisticsCache { +public: + /// Get the SummaryStatistics object for a given provider name, or insert + /// if statistics for that provider is not in the map. + SummaryStatisticsSP + GetSummaryStatisticsForProvider(lldb_private::TypeSummaryImpl &provider) { + std::lock_guard guard(m_map_mutex); + if (auto iterator = m_summary_stats_map.find(provider.GetName()); + iterator != m_summary_stats_map.end()) + return iterator->second; + + auto it = m_summary_stats_map.try_emplace( + provider.GetName(), + std::make_shared(provider.GetName(), + provider.GetSummaryKindName())); + return it.first->second; + } + + llvm::json::Value ToJSON(); + + void Reset(); + +private: + llvm::StringMap m_summary_stats_map; + std::mutex m_map_mutex; +}; + /// A class that represents statistics for a since lldb_private::Target. class TargetStats { public: @@ -184,10 +271,13 @@ class TargetStats { void SetFirstPrivateStopTime(); void SetFirstPublicStopTime(); void IncreaseSourceMapDeduceCount(); + void IncreaseSourceRealpathAttemptCount(uint32_t count); + void IncreaseSourceRealpathCompatibleCount(uint32_t count); StatsDuration &GetCreateTime() { return m_create_time; } StatsSuccessFail &GetExpressionStats() { return m_expr_eval; } StatsSuccessFail &GetFrameVariableStats() { return m_frame_var; } + void Reset(Target &target); protected: StatsDuration m_create_time; @@ -198,6 +288,8 @@ class TargetStats { StatsSuccessFail m_frame_var{"frameVariable"}; std::vector m_module_identifiers; uint32_t m_source_map_deduce_count = 0; + uint32_t m_source_realpath_attempt_count = 0; + uint32_t m_source_realpath_compatible_count = 0; void CollectStats(Target &target); }; @@ -226,6 +318,16 @@ class DebuggerStats { ReportStatistics(Debugger &debugger, Target *target, const lldb_private::StatisticsOptions &options); + /// Reset metrics associated with one or all targets in a debugger. + /// + /// \param debugger + /// The debugger to reset the target list from if \a target is NULL. + /// + /// \param target + /// The target to reset statistics for, or if null, reset statistics + /// for all targets + static void ResetStatistics(Debugger &debugger, Target *target); + protected: // Collecting stats can be set to true to collect stats that are expensive // to collect. By default all stats that are cheap to collect are enabled. diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 84f59dbdea715..be61833d7a5d3 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -41,6 +41,7 @@ #include "lldb/Utility/Args.h" #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/RealpathPrefixes.h" #include "lldb/Utility/Timeout.h" #include "lldb/lldb-public.h" @@ -138,6 +139,8 @@ class TargetProperties : public Properties { InlineStrategy GetInlineStrategy() const; + RealpathPrefixes GetSourceRealpathPrefixes() const; + llvm::StringRef GetArg0() const; void SetArg0(llvm::StringRef arg); @@ -1327,6 +1330,10 @@ class Target : public std::enable_shared_from_this, void ClearAllLoadedSections(); + lldb_private::SummaryStatisticsSP GetSummaryStatisticsSPForProviderName( + lldb_private::TypeSummaryImpl &summary_provider); + lldb_private::SummaryStatisticsCache &GetSummaryStatisticsCache(); + /// Set the \a Trace object containing processor trace information of this /// target. /// @@ -1661,6 +1668,7 @@ class Target : public std::enable_shared_from_this, std::string m_label; ModuleList m_images; ///< The list of images for this process (shared /// libraries and anything dynamically loaded). + SummaryStatisticsCache m_summary_statistics_cache; SectionLoadHistory m_section_load_history; BreakpointList m_breakpoint_list; BreakpointList m_internal_breakpoint_list; @@ -1727,6 +1735,8 @@ class Target : public std::enable_shared_from_this, llvm::json::Value ReportStatistics(const lldb_private::StatisticsOptions &options); + void ResetStatistics(); + TargetStats &GetStatistics() { return m_stats; } protected: diff --git a/lldb/include/lldb/Utility/FileSpecList.h b/lldb/include/lldb/Utility/FileSpecList.h index 6eb3bb9971f13..d091a9246e082 100644 --- a/lldb/include/lldb/Utility/FileSpecList.h +++ b/lldb/include/lldb/Utility/FileSpecList.h @@ -64,10 +64,16 @@ class SupportFileList { /// \param[in] file /// The file specification to search for. /// + /// \param[in] realpath_prefixes + /// Paths that start with one of the prefixes in this list will be + /// realpath'ed to resolve any symlinks. + /// /// \return /// The index of the file that matches \a file if it is found, /// else UINT32_MAX is returned. - size_t FindCompatibleIndex(size_t idx, const FileSpec &file) const; + size_t + FindCompatibleIndex(size_t idx, const FileSpec &file, + RealpathPrefixes *realpath_prefixes = nullptr) const; template void EmplaceBack(Args &&...args) { m_files.push_back( diff --git a/lldb/include/lldb/Utility/RealpathPrefixes.h b/lldb/include/lldb/Utility/RealpathPrefixes.h new file mode 100644 index 0000000000000..daa2a479d6a24 --- /dev/null +++ b/lldb/include/lldb/Utility/RealpathPrefixes.h @@ -0,0 +1,77 @@ +//===-- RealpathPrefixes.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UTILITY_REALPATHPREFIXES_H +#define LLDB_UTILITY_REALPATHPREFIXES_H + +#include "lldb/lldb-forward.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include +#include +#include + +namespace lldb_private { + +class RealpathPrefixes { +public: + /// \param[in] file_spec_list + /// Prefixes are obtained from FileSpecList, through FileSpec::GetPath(), + /// which ensures that the paths are normalized. For example: + /// "./foo/.." -> "" + /// "./foo/../bar" -> "bar" + /// + /// \param[in] fs + /// An optional filesystem to use for realpath'ing. If not set, the real + /// filesystem will be used. + explicit RealpathPrefixes(const FileSpecList &file_spec_list, + llvm::IntrusiveRefCntPtr fs = + llvm::vfs::getRealFileSystem()); + + std::optional ResolveSymlinks(const FileSpec &file_spec); + + // If/when Statistics.h/cpp is moved into Utility, we can remove these + // methods, hold a (weak) pointer to `TargetStats` and directly increment + // on that object. + void IncreaseSourceRealpathAttemptCount() { + ++m_source_realpath_attempt_count; + } + uint32_t GetSourceRealpathAttemptCount() const { + return m_source_realpath_attempt_count; + } + void IncreaseSourceRealpathCompatibleCount() { + ++m_source_realpath_compatible_count; + } + uint32_t GetSourceRealpathCompatibleCount() const { + return m_source_realpath_compatible_count; + } + +private: + // Paths that start with one of the prefixes in this list will be realpath'ed + // to resolve any symlinks. + // + // Wildcard prefixes: + // - "" (empty string) will match all paths. + // - "/" will match all absolute paths. + std::vector m_prefixes; + + // The filesystem to use for realpath'ing. + llvm::IntrusiveRefCntPtr m_fs; + + // The optional Target instance to gather statistics. + lldb::TargetWP m_target; + + // Statistics that we temprarily hold here, to be gathered into TargetStats + uint32_t m_source_realpath_attempt_count = 0; + uint32_t m_source_realpath_compatible_count = 0; +}; + +} // namespace lldb_private + +#endif // LLDB_UTILITY_REALPATHPREFIXES_H diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 199a06a7166f6..c36d80b55a8d8 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -777,6 +777,7 @@ enum SectionType { eSectionTypeDWARFDebugTuIndex, eSectionTypeCTF, eSectionTypeLLDBTypeSummaries, + eSectionTypeLLDBFormatters, eSectionTypeSwiftModules, }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 1024501e05bca..337eff696fcf3 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -175,6 +175,7 @@ class Queue; class QueueImpl; class QueueItem; class REPL; +class RealpathPrefixes; class RecognizedStackFrame; class RegisterCheckpoint; class RegisterContext; diff --git a/lldb/include/lldb/lldb-private-types.h b/lldb/include/lldb/lldb-private-types.h index 055eea9f6456d..b82a2b8aa0574 100644 --- a/lldb/include/lldb/lldb-private-types.h +++ b/lldb/include/lldb/lldb-private-types.h @@ -12,6 +12,7 @@ #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" #include @@ -26,6 +27,8 @@ class Platform; class ExecutionContext; class RegisterFlags; +typedef llvm::SmallString<256> PathSmallString; + typedef llvm::sys::DynamicLibrary (*LoadPluginCallbackType)( const lldb::DebuggerSP &debugger_sp, const FileSpec &spec, Status &error); diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index 1cd5fec461460..e3fe46dada18e 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -268,10 +268,6 @@ def parseOptionsAndInitTestdirs(): if args.make: configuration.make_path = args.make - elif platform_system == "FreeBSD" or platform_system == "NetBSD": - configuration.make_path = "gmake" - else: - configuration.make_path = "make" if args.dsymutil: configuration.dsymutil = args.dsymutil diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index 3d16f32251084..cc9947cbd7256 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -1682,6 +1682,12 @@ SBTypeSynthetic SBDebugger::GetSyntheticForType(SBTypeNameSpecifier type_name) { DataVisualization::GetSyntheticForType(type_name.GetSP())); } +void SBDebugger::ResetStatistics() { + LLDB_INSTRUMENT_VA(this); + if (m_opaque_sp) + DebuggerStats::ResetStatistics(*m_opaque_sp, nullptr); +} + static llvm::ArrayRef GetCategoryArray(const char **categories) { if (categories == nullptr) return {}; diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 6794b33ff83d8..57b42da429d9a 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -225,6 +225,13 @@ SBStructuredData SBTarget::GetStatistics(SBStatisticsOptions options) { return data; } +void SBTarget::ResetStatistics() { + LLDB_INSTRUMENT_VA(this); + TargetSP target_sp(GetSP()); + if (target_sp) + DebuggerStats::ResetStatistics(target_sp->GetDebugger(), target_sp.get()); +} + void SBTarget::SetCollectingStats(bool v) { LLDB_INSTRUMENT_VA(this, v); diff --git a/lldb/source/API/SBTypeSummary.cpp b/lldb/source/API/SBTypeSummary.cpp index a5e87188c9f63..856ee0ed3175b 100644 --- a/lldb/source/API/SBTypeSummary.cpp +++ b/lldb/source/API/SBTypeSummary.cpp @@ -343,6 +343,7 @@ bool SBTypeSummary::IsEqualTo(lldb::SBTypeSummary &rhs) { case TypeSummaryImpl::Kind::eCallback: return llvm::dyn_cast(m_opaque_sp.get()) == llvm::dyn_cast(rhs.m_opaque_sp.get()); + case TypeSummaryImpl::Kind::eBytecode: case TypeSummaryImpl::Kind::eScript: if (IsFunctionCode() != rhs.IsFunctionCode()) return false; diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp index 3268ce0b6857d..9339e12a29f79 100644 --- a/lldb/source/Breakpoint/Breakpoint.cpp +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -1138,3 +1138,5 @@ json::Value Breakpoint::GetStatistics() { } return json::Value(std::move(bp)); } + +void Breakpoint::ResetStatistics() { m_resolve_time.reset(); } diff --git a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp index 8ac6eceb446e6..8e7386df03e9b 100644 --- a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -15,6 +15,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/RealpathPrefixes.h" #include "lldb/Utility/StreamString.h" #include @@ -295,16 +296,25 @@ Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback( const uint32_t line = m_location_spec.GetLine().value_or(0); const std::optional column = m_location_spec.GetColumn(); + Target &target = GetBreakpoint()->GetTarget(); + RealpathPrefixes realpath_prefixes = target.GetSourceRealpathPrefixes(); + const size_t num_comp_units = context.module_sp->GetNumCompileUnits(); for (size_t i = 0; i < num_comp_units; i++) { CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i)); if (cu_sp) { if (filter.CompUnitPasses(*cu_sp)) cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything, - sc_list); + sc_list, &realpath_prefixes); } } + // Gather stats into the Target + target.GetStatistics().IncreaseSourceRealpathAttemptCount( + realpath_prefixes.GetSourceRealpathAttemptCount()); + target.GetStatistics().IncreaseSourceRealpathCompatibleCount( + realpath_prefixes.GetSourceRealpathCompatibleCount()); + FilterContexts(sc_list); DeduceSourceMapping(sc_list); diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 679c9add381bc..ccc6af2a83e4a 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -88,8 +88,10 @@ Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) { // Swift 4.2 used "$S" and "_$S". // Swift 5 and onward uses "$s" and "_$s". // Swift also uses "@__swiftmacro_" as a prefix for mangling filenames. + // Embedded Swift introduced "$e" and "_$e" as Swift mangling prefixes. if (name.starts_with("$S") || name.starts_with("_$S") || name.starts_with("$s") || name.starts_with("_$s") || + name.starts_with("$e") || name.starts_with("_$e") || name.starts_with("@__swiftmacro_")) return Mangled::eManglingSchemeSwift; diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 934e435cc7e89..d6abfff05bea8 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1758,6 +1758,14 @@ bool Module::MergeArchitecture(const ArchSpec &arch_spec) { return SetArchitecture(merged_arch); } +void Module::ResetStatistics() { + m_symtab_parse_time.reset(); + m_symtab_index_time.reset(); + SymbolFile *sym_file = GetSymbolFile(); + if (sym_file) + sym_file->ResetStatistics(); +} + llvm::VersionTuple Module::GetVersion() { if (ObjectFile *obj_file = GetObjectFile()) return obj_file->GetVersion(); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index fbc6512cc465c..d696d492d81e1 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -151,10 +151,12 @@ const char *Section::GetTypeAsCString() const { return "ctf"; case eSectionTypeLLDBTypeSummaries: return "lldb-type-summaries"; - case eSectionTypeOther: - return "regular"; + case eSectionTypeLLDBFormatters: + return "lldb-formatters"; case eSectionTypeSwiftModules: return "swift-modules"; + case eSectionTypeOther: + return "regular"; } return "unknown"; } @@ -462,6 +464,7 @@ bool Section::ContainsOnlyDebugInfo() const { case eSectionTypeDWARFGNUDebugAltLink: case eSectionTypeCTF: case eSectionTypeLLDBTypeSummaries: + case eSectionTypeLLDBFormatters: case eSectionTypeSwiftModules: return true; } diff --git a/lldb/source/DataFormatters/CMakeLists.txt b/lldb/source/DataFormatters/CMakeLists.txt index 13faf65227d2c..91b10ba9e0ac8 100644 --- a/lldb/source/DataFormatters/CMakeLists.txt +++ b/lldb/source/DataFormatters/CMakeLists.txt @@ -5,7 +5,9 @@ add_lldb_library(lldbDataFormatters NO_PLUGIN_DEPENDENCIES FormatCache.cpp FormatClasses.cpp FormatManager.cpp + FormatterBytecode.cpp FormattersHelpers.cpp + FormatterSection.cpp LanguageCategory.cpp StringPrinter.cpp TypeCategory.cpp diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index 889135180fec9..6b199ff04f389 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -766,6 +766,64 @@ void FormatManager::LoadSystemFormatters() { sys_category_sp->AddTypeSummary(R"(^(unsigned )?char ?(\*|\[\])$)", eFormatterMatchRegex, string_format); + /* TO_UPSTREAM(BoundsSafety) ON */ + TypeSummaryImpl::Flags bounds_safety_flags; + bounds_safety_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + // Since we have exact match summeries for char* and unsigned char* in order + // to have BoundsSafety summeries choosen before those for char* and unsinged + // char* they must be: + // - exact matches as well + // - added afterward since they will be searched in reverse order of + // addition + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __bidi_indexable summary provider", + ConstString("char *__bidi_indexable"), bounds_safety_flags); + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __indexable summary provider", + ConstString("char *__indexable"), bounds_safety_flags); + + AddCXXSummary( + sys_category_sp, lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __bidi_indexable summary provider", + ConstString("unsigned char *__bidi_indexable"), bounds_safety_flags); + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __indexable summary provider", + ConstString("unsigned char *__indexable"), bounds_safety_flags); + + // These will catch everything else for BoundsSafety pointers. + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __bidi_indexable summary provider", + ConstString("__bidi_indexable"), bounds_safety_flags, true); + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyPointer, + "BoundsSafety __indexable summary provider", + ConstString("__indexable"), bounds_safety_flags, true); + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyAttrPointer, + "BoundsSafety counted_by summary provider", + ConstString("__bounds_safety::counted_by"), bounds_safety_flags, true); + AddCXXSummary(sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyAttrPointer, + "BoundsSafety sized_by summary provider", + ConstString("__bounds_safety::sized_by"), bounds_safety_flags, true); + AddCXXSummary( + sys_category_sp, + lldb_private::formatters::FormatBoundsSafetyDynamicRangeAttrPointer, + "BoundsSafety dynamic range summary provider", + ConstString("__bounds_safety::dynamic_range"), bounds_safety_flags, true); + /* TO_UPSTREAM(BoundsSafety) OFF */ + sys_category_sp->AddTypeSummary(R"(^((un)?signed )?char ?\[[0-9]+\]$)", eFormatterMatchRegex, string_array_format); diff --git a/lldb/source/DataFormatters/FormatterBytecode.cpp b/lldb/source/DataFormatters/FormatterBytecode.cpp new file mode 100644 index 0000000000000..ace7accc016eb --- /dev/null +++ b/lldb/source/DataFormatters/FormatterBytecode.cpp @@ -0,0 +1,575 @@ +//===-- FormatterBytecode.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatterBytecode.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/ValueObject/ValueObject.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadicDetails.h" + +using namespace lldb; +namespace lldb_private { + +std::string toString(FormatterBytecode::OpCodes op) { + switch (op) { +#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \ + case OP: { \ + const char *s = MNEMONIC; \ + return s ? s : #NAME; \ + } +#include "FormatterBytecode.def" +#undef DEFINE_SIGNATURE + } + return llvm::utostr(op); +} + +std::string toString(FormatterBytecode::Selectors sel) { + switch (sel) { +#define DEFINE_SELECTOR(ID, NAME) \ + case ID: \ + return "@" #NAME; +#include "FormatterBytecode.def" +#undef DEFINE_SIGNATURE + } + return "@" + llvm::utostr(sel); +} + +std::string toString(FormatterBytecode::Signatures sig) { + switch (sig) { +#define DEFINE_SIGNATURE(ID, NAME) \ + case ID: \ + return "@" #NAME; +#include "FormatterBytecode.def" +#undef DEFINE_SIGNATURE + } + return llvm::utostr(sig); +} + +std::string toString(const FormatterBytecode::DataStack &data) { + std::string s; + llvm::raw_string_ostream os(s); + os << "[ "; + for (auto &d : data) { + if (auto s = std::get_if(&d)) + os << '"' << *s << '"'; + else if (auto u = std::get_if(&d)) + os << *u << 'u'; + else if (auto i = std::get_if(&d)) + os << *i; + else if (auto valobj = std::get_if(&d)) { + if (!valobj->get()) + os << "null"; + else + os << "object(" << valobj->get()->GetValueAsCString() << ')'; + } else if (auto type = std::get_if(&d)) { + os << '(' << type->GetTypeName(true) << ')'; + } else if (auto sel = std::get_if(&d)) { + os << toString(*sel); + } + os << ' '; + } + os << ']'; + return s; +} + +namespace FormatterBytecode { + +/// Implement the @format function. +static llvm::Error FormatImpl(DataStack &data) { + auto fmt = data.Pop(); + auto replacements = + llvm::formatv_object_base::parseFormatString(fmt); + std::string s; + llvm::raw_string_ostream os(s); + unsigned num_args = 0; + for (const auto &r : replacements) + if (r.Type == llvm::ReplacementType::Format) + num_args = std::max(num_args, (unsigned)r.Index + 1); + + if (data.size() < num_args) + return llvm::createStringError("not enough arguments"); + + for (const auto &r : replacements) { + if (r.Type == llvm::ReplacementType::Literal) { + os << r.Spec; + continue; + } + using namespace llvm::support::detail; + auto arg = data[data.size() - num_args + r.Index]; + auto format = [&](format_adapter &&adapter) { + llvm::FmtAlign Align(adapter, r.Where, r.Align, r.Pad); + Align.format(os, r.Options); + }; + + if (auto s = std::get_if(&arg)) + format(build_format_adapter(s->c_str())); + else if (auto u = std::get_if(&arg)) + format(build_format_adapter(u)); + else if (auto i = std::get_if(&arg)) + format(build_format_adapter(i)); + else if (auto valobj = std::get_if(&arg)) { + if (!valobj->get()) + format(build_format_adapter("null object")); + else + format(build_format_adapter(valobj->get()->GetValueAsCString())); + } else if (auto type = std::get_if(&arg)) + format(build_format_adapter(type->GetDisplayTypeName())); + else if (auto sel = std::get_if(&arg)) + format(build_format_adapter(toString(*sel))); + } + data.Push(s); + return llvm::Error::success(); +} + +static llvm::Error TypeCheck(llvm::ArrayRef data, + DataType type) { + if (data.size() < 1) + return llvm::createStringError("not enough elements on data stack"); + + auto &elem = data.back(); + switch (type) { + case Any: + break; + case String: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected String"); + break; + case UInt: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected UInt"); + break; + case Int: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected Int"); + break; + case Object: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected Object"); + break; + case Type: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected Type"); + break; + case Selector: + if (!std::holds_alternative(elem)) + return llvm::createStringError("expected Selector"); + break; + } + return llvm::Error::success(); +} + +static llvm::Error TypeCheck(llvm::ArrayRef data, + DataType type1, DataType type2) { + if (auto error = TypeCheck(data, type2)) + return error; + return TypeCheck(data.drop_back(), type1); +} + +static llvm::Error TypeCheck(llvm::ArrayRef data, + DataType type1, DataType type2, DataType type3) { + if (auto error = TypeCheck(data, type3)) + return error; + return TypeCheck(data.drop_back(1), type2, type1); +} + +llvm::Error Interpret(std::vector &control, + DataStack &data, Selectors sel) { + if (control.empty()) + return llvm::Error::success(); + // Since the only data types are single endian and ULEBs, the + // endianness should not matter. + llvm::DataExtractor cur_block(control.back(), true, 64); + llvm::DataExtractor::Cursor pc(0); + + while (!control.empty()) { + /// Activate the top most block from the control stack. + auto activate_block = [&]() { + // Save the return address. + if (control.size() > 1) + control[control.size() - 2] = cur_block.getData().drop_front(pc.tell()); + cur_block = llvm::DataExtractor(control.back(), true, 64); + if (pc) + pc = llvm::DataExtractor::Cursor(0); + }; + + /// Fetch the next byte in the instruction stream. + auto next_byte = [&]() -> uint8_t { + // At the end of the current block? + while (pc.tell() >= cur_block.size() && !control.empty()) { + if (control.size() == 1) { + control.pop_back(); + return 0; + } + control.pop_back(); + activate_block(); + } + + // Fetch the next instruction. + return cur_block.getU8(pc); + }; + + // Fetch the next opcode. + OpCodes opcode = (OpCodes)next_byte(); + if (control.empty() || !pc) + return pc.takeError(); + + LLDB_LOGV(GetLog(LLDBLog::DataFormatters), + "[eval {0}] opcode={1}, control={2}, data={3}", toString(sel), + toString(opcode), control.size(), toString(data)); + + // Various shorthands to improve the readability of error handling. +#define TYPE_CHECK(...) \ + if (auto error = TypeCheck(data, __VA_ARGS__)) \ + return error; + + auto error = [&](llvm::Twine msg) { + return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")"); + }; + + switch (opcode) { + // Data stack manipulation. + case op_dup: + TYPE_CHECK(Any); + data.Push(data.back()); + continue; + case op_drop: + TYPE_CHECK(Any); + data.pop_back(); + continue; + case op_pick: { + TYPE_CHECK(UInt); + uint64_t idx = data.Pop(); + if (idx >= data.size()) + return error("index out of bounds"); + data.Push(data[idx]); + continue; + } + case op_over: + TYPE_CHECK(Any, Any); + data.Push(data[data.size() - 2]); + continue; + case op_swap: { + TYPE_CHECK(Any, Any); + auto x = data.PopAny(); + auto y = data.PopAny(); + data.Push(x); + data.Push(y); + continue; + } + case op_rot: { + TYPE_CHECK(Any, Any, Any); + auto z = data.PopAny(); + auto y = data.PopAny(); + auto x = data.PopAny(); + data.Push(z); + data.Push(x); + data.Push(y); + continue; + } + + // Control stack manipulation. + case op_begin: { + uint64_t length = cur_block.getULEB128(pc); + if (!pc) + return pc.takeError(); + llvm::StringRef block = cur_block.getBytes(pc, length); + if (!pc) + return pc.takeError(); + control.push_back(block); + continue; + } + case op_if: + TYPE_CHECK(UInt); + if (data.Pop() != 0) { + if (!cur_block.size()) + return error("empty control stack"); + activate_block(); + } else + control.pop_back(); + continue; + case op_ifelse: + TYPE_CHECK(UInt); + if (cur_block.size() < 2) + return error("empty control stack"); + if (data.Pop() == 0) + control[control.size() - 2] = control.back(); + control.pop_back(); + activate_block(); + continue; + + // Literals. + case op_lit_uint: + data.Push(cur_block.getULEB128(pc)); + continue; + case op_lit_int: + data.Push(cur_block.getSLEB128(pc)); + continue; + case op_lit_selector: + data.Push(Selectors(cur_block.getU8(pc))); + continue; + case op_lit_string: { + uint64_t length = cur_block.getULEB128(pc); + llvm::StringRef bytes = cur_block.getBytes(pc, length); + data.Push(bytes.str()); + continue; + } + case op_as_uint: { + TYPE_CHECK(Int); + uint64_t casted; + int64_t val = data.Pop(); + memcpy(&casted, &val, sizeof(val)); + data.Push(casted); + continue; + } + case op_as_int: { + TYPE_CHECK(UInt); + int64_t casted; + uint64_t val = data.Pop(); + memcpy(&casted, &val, sizeof(val)); + data.Push(casted); + continue; + } + case op_is_null: { + TYPE_CHECK(Object); + data.Push(data.Pop() ? (uint64_t)0 : (uint64_t)1); + continue; + } + + // Arithmetic, logic, etc. +#define BINOP_IMPL(OP, CHECK_ZERO) \ + { \ + TYPE_CHECK(Any, Any); \ + auto y = data.PopAny(); \ + if (std::holds_alternative(y)) { \ + if (CHECK_ZERO && !std::get(y)) \ + return error(#OP " by zero"); \ + TYPE_CHECK(UInt); \ + data.Push((uint64_t)(data.Pop() OP std::get(y))); \ + } else if (std::holds_alternative(y)) { \ + if (CHECK_ZERO && !std::get(y)) \ + return error(#OP " by zero"); \ + TYPE_CHECK(Int); \ + data.Push((int64_t)(data.Pop() OP std::get(y))); \ + } else \ + return error("unsupported data types"); \ + } +#define BINOP(OP) BINOP_IMPL(OP, false) +#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true) + case op_plus: + BINOP(+); + continue; + case op_minus: + BINOP(-); + continue; + case op_mul: + BINOP(*); + continue; + case op_div: + BINOP_CHECKZERO(/); + continue; + case op_mod: + BINOP_CHECKZERO(%); + continue; + case op_shl: +#define SHIFTOP(OP, LEFT) \ + { \ + TYPE_CHECK(Any, UInt); \ + uint64_t y = data.Pop(); \ + if (y > 64) \ + return error("shift out of bounds"); \ + if (std::holds_alternative(data.back())) { \ + uint64_t x = data.Pop(); \ + data.Push(x OP y); \ + } else if (std::holds_alternative(data.back())) { \ + int64_t x = data.Pop(); \ + if (x < 0 && LEFT) \ + return error("left shift of negative value"); \ + if (y > 64) \ + return error("shift out of bounds"); \ + data.Push(x OP y); \ + } else \ + return error("unsupported data types"); \ + } + SHIFTOP(<<, true); + continue; + case op_shr: + SHIFTOP(>>, false); + continue; + case op_and: + BINOP(&); + continue; + case op_or: + BINOP(|); + continue; + case op_xor: + BINOP(^); + continue; + case op_not: + TYPE_CHECK(UInt); + data.Push(~data.Pop()); + continue; + case op_eq: + BINOP(==); + continue; + case op_neq: + BINOP(!=); + continue; + case op_lt: + BINOP(<); + continue; + case op_gt: + BINOP(>); + continue; + case op_le: + BINOP(<=); + continue; + case op_ge: + BINOP(>=); + continue; + case op_call: { + TYPE_CHECK(Selector); + Selectors sel = data.Pop(); + + // Shorthand to improve readability. +#define POP_VALOBJ(VALOBJ) \ + auto VALOBJ = data.Pop(); \ + if (!VALOBJ) \ + return error("null object"); + + auto sel_error = [&](const char *msg) { + return llvm::createStringError("{0} (opcode={1}, selector={2})", msg, + toString(opcode).c_str(), + toString(sel).c_str()); + }; + + switch (sel) { + case sel_summary: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + const char *summary = valobj->GetSummaryAsCString(); + data.Push(summary ? std::string(valobj->GetSummaryAsCString()) + : std::string()); + break; + } + case sel_get_num_children: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + auto result = valobj->GetNumChildren(); + if (!result) + return result.takeError(); + data.Push((uint64_t)*result); + break; + } + case sel_get_child_at_index: { + TYPE_CHECK(Object, UInt); + auto index = data.Pop(); + POP_VALOBJ(valobj); + data.Push(valobj->GetChildAtIndex(index)); + break; + } + case sel_get_child_with_name: { + TYPE_CHECK(Object, String); + auto name = data.Pop(); + POP_VALOBJ(valobj); + data.Push(valobj->GetChildMemberWithName(name)); + break; + } + case sel_get_child_index: { + TYPE_CHECK(Object, String); + auto name = data.Pop(); + POP_VALOBJ(valobj); + data.Push((uint64_t)valobj->GetIndexOfChildWithName(name)); + break; + } + case sel_get_type: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + // FIXME: do we need to control dynamic type resolution? + data.Push(valobj->GetTypeImpl().GetCompilerType(false)); + break; + } + case sel_get_template_argument_type: { + TYPE_CHECK(Type, UInt); + auto index = data.Pop(); + auto type = data.Pop(); + // FIXME: There is more code in SBType::GetTemplateArgumentType(). + data.Push(type.GetTypeTemplateArgument(index, true)); + break; + } + case sel_get_value: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + data.Push(std::string(valobj->GetValueAsCString())); + break; + } + case sel_get_value_as_unsigned: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + bool success; + uint64_t val = valobj->GetValueAsUnsigned(0, &success); + data.Push(val); + if (!success) + return sel_error("failed to get value"); + break; + } + case sel_get_value_as_signed: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + bool success; + int64_t val = valobj->GetValueAsSigned(0, &success); + data.Push(val); + if (!success) + return sel_error("failed to get value"); + break; + } + case sel_get_value_as_address: { + TYPE_CHECK(Object); + POP_VALOBJ(valobj); + bool success; + uint64_t addr = valobj->GetValueAsUnsigned(0, &success); + if (!success) + return sel_error("failed to get value"); + if (auto process_sp = valobj->GetProcessSP()) + addr = process_sp->FixDataAddress(addr); + data.Push(addr); + break; + } + case sel_cast: { + TYPE_CHECK(Object, Type); + auto type = data.Pop(); + POP_VALOBJ(valobj); + data.Push(valobj->Cast(type)); + break; + } + case sel_strlen: { + TYPE_CHECK(String); + data.Push((uint64_t)data.Pop().size()); + break; + } + case sel_fmt: { + TYPE_CHECK(String); + if (auto error = FormatImpl(data)) + return error; + break; + } + default: + return sel_error("selector not implemented"); + } + continue; + } + } + return error("opcode not implemented"); + } + return pc.takeError(); +} +} // namespace FormatterBytecode + +} // namespace lldb_private diff --git a/lldb/source/DataFormatters/FormatterBytecode.def b/lldb/source/DataFormatters/FormatterBytecode.def new file mode 100644 index 0000000000000..c6645631fa006 --- /dev/null +++ b/lldb/source/DataFormatters/FormatterBytecode.def @@ -0,0 +1,101 @@ +//===-- FormatterBytecode.def -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef DEFINE_OPCODE +#define DEFINE_OPCODE(OP, MNEMONIC, NAME) +#endif +#ifndef DEFINE_SELECTOR +#define DEFINE_SELECTOR(ID, NAME) +#endif +#ifndef DEFINE_SIGNATURE +#define DEFINE_SIGNATURE(ID, NAME) +#endif + +// Opcodes. +DEFINE_OPCODE(0x01, "dup", dup) +DEFINE_OPCODE(0x02, "drop", drop) +DEFINE_OPCODE(0x03, "pick", pick) +DEFINE_OPCODE(0x04, "over", over) +DEFINE_OPCODE(0x05, "swap", swap) +DEFINE_OPCODE(0x06, "rot", rot) + +DEFINE_OPCODE(0x10, "{", begin) +DEFINE_OPCODE(0x11, "if", if) +DEFINE_OPCODE(0x12, "ifelse", ifelse) + +DEFINE_OPCODE(0x20, nullptr, lit_uint) +DEFINE_OPCODE(0x21, nullptr, lit_int) +DEFINE_OPCODE(0x22, nullptr, lit_string) +DEFINE_OPCODE(0x23, nullptr, lit_selector) + +DEFINE_OPCODE(0x2a, "as_int", as_int) +DEFINE_OPCODE(0x2b, "as_uint", as_uint) +DEFINE_OPCODE(0x2c, "is_null", is_null) + +DEFINE_OPCODE(0x30, "+", plus) +DEFINE_OPCODE(0x31, "-", minus) +DEFINE_OPCODE(0x32, "*", mul) +DEFINE_OPCODE(0x33, "/", div) +DEFINE_OPCODE(0x34, "%", mod) +DEFINE_OPCODE(0x35, "<<", shl) +DEFINE_OPCODE(0x36, ">>", shr) + +DEFINE_OPCODE(0x40, "&", and) +DEFINE_OPCODE(0x41, "|", or) +DEFINE_OPCODE(0x42, "^", xor) +DEFINE_OPCODE(0x43, "~", not) + +DEFINE_OPCODE(0x50, "=", eq) +DEFINE_OPCODE(0x51, "!=", neq) +DEFINE_OPCODE(0x52, "<", lt) +DEFINE_OPCODE(0x53, ">", gt) +DEFINE_OPCODE(0x54, "=<", le) +DEFINE_OPCODE(0x55, ">=", ge) + +DEFINE_OPCODE(0x60, "call", call) + +// Selectors. +DEFINE_SELECTOR(0x00, summary) +DEFINE_SELECTOR(0x01, type_summary) + +DEFINE_SELECTOR(0x10, get_num_children) +DEFINE_SELECTOR(0x11, get_child_at_index) +DEFINE_SELECTOR(0x12, get_child_with_name) +DEFINE_SELECTOR(0x13, get_child_index) +DEFINE_SELECTOR(0x15, get_type) +DEFINE_SELECTOR(0x16, get_template_argument_type) +DEFINE_SELECTOR(0x17, cast) + +DEFINE_SELECTOR(0x20, get_value) +DEFINE_SELECTOR(0x21, get_value_as_unsigned) +DEFINE_SELECTOR(0x22, get_value_as_signed) +DEFINE_SELECTOR(0x23, get_value_as_address) + +DEFINE_SELECTOR(0x40, read_memory_byte) +DEFINE_SELECTOR(0x41, read_memory_uint32) +DEFINE_SELECTOR(0x42, read_memory_int32) +DEFINE_SELECTOR(0x43, read_memory_unsigned) +DEFINE_SELECTOR(0x44, read_memory_signed) +DEFINE_SELECTOR(0x45, read_memory_address) +DEFINE_SELECTOR(0x46, read_memory) + +DEFINE_SELECTOR(0x50, fmt) +DEFINE_SELECTOR(0x51, sprintf) +DEFINE_SELECTOR(0x52, strlen) + +// Formatter signatures. +DEFINE_SIGNATURE(0, summary) +DEFINE_SIGNATURE(1, init) +DEFINE_SIGNATURE(2, get_num_children) +DEFINE_SIGNATURE(3, get_child_index) +DEFINE_SIGNATURE(4, get_child_at_index) +DEFINE_SIGNATURE(5, get_value) + +#undef DEFINE_OPCODE +#undef DEFINE_SELECTOR +#undef DEFINE_SIGNATURE diff --git a/lldb/source/DataFormatters/FormatterBytecode.h b/lldb/source/DataFormatters/FormatterBytecode.h new file mode 100644 index 0000000000000..21454d9c7e231 --- /dev/null +++ b/lldb/source/DataFormatters/FormatterBytecode.h @@ -0,0 +1,64 @@ +//===-- FormatterBytecode.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Symbol/CompilerType.h" + +namespace lldb_private { + +namespace FormatterBytecode { + +enum DataType : uint8_t { Any, String, Int, UInt, Object, Type, Selector }; + +enum OpCodes : uint8_t { +#define DEFINE_OPCODE(OP, MNEMONIC, NAME) op_##NAME = OP, +#include "FormatterBytecode.def" +#undef DEFINE_OPCODE +}; + +enum Selectors : uint8_t { +#define DEFINE_SELECTOR(ID, NAME) sel_##NAME = ID, +#include "FormatterBytecode.def" +#undef DEFINE_SELECTOR +}; + +enum Signatures : uint8_t { +#define DEFINE_SIGNATURE(ID, NAME) sig_##NAME = ID, +#include "FormatterBytecode.def" +#undef DEFINE_SIGNATURE +}; + +using ControlStackElement = llvm::StringRef; +using DataStackElement = + std::variant; +struct DataStack : public std::vector { + DataStack() = default; + DataStack(lldb::ValueObjectSP initial_value) + : std::vector({initial_value}) {} + void Push(DataStackElement el) { push_back(el); } + template T Pop() { + T el = std::get(back()); + pop_back(); + return el; + } + DataStackElement PopAny() { + DataStackElement el = back(); + pop_back(); + return el; + } +}; +llvm::Error Interpret(std::vector &control, + DataStack &data, Selectors sel); +} // namespace FormatterBytecode + +std::string toString(FormatterBytecode::OpCodes op); +std::string toString(FormatterBytecode::Selectors sel); +std::string toString(FormatterBytecode::Signatures sig); + +} // namespace lldb_private diff --git a/lldb/source/DataFormatters/FormatterSection.cpp b/lldb/source/DataFormatters/FormatterSection.cpp new file mode 100644 index 0000000000000..1de633f4998e0 --- /dev/null +++ b/lldb/source/DataFormatters/FormatterSection.cpp @@ -0,0 +1,170 @@ +//===-- FormatterBytecode.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatterBytecode.h" +#include "lldb/Core/Module.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Utility/LLDBLog.h" + +using namespace lldb; + +namespace lldb_private { +static void ForEachFormatterInModule( + Module &module, SectionType section_type, + std::function fn) { + auto *sections = module.GetSectionList(); + if (!sections) + return; + + auto section_sp = sections->FindSectionByType(section_type, true); + if (!section_sp) + return; + + TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString("default"), category); + + // The type summary record is serialized as follows. + // + // Each record contains, in order: + // * Version number of the record format + // * The remaining size of the record + // * The size of the type identifier + // * The type identifier, either a type name, or a regex + // * The size of the entry + // * The entry + // + // Integers are encoded using ULEB. + // + // Strings are encoded with first a length (ULEB), then the string contents, + // and lastly a null terminator. The length includes the null. + + DataExtractor lldb_extractor; + auto section_size = section_sp->GetSectionData(lldb_extractor); + llvm::DataExtractor section = lldb_extractor.GetAsLLVM(); + bool le = section.isLittleEndian(); + uint8_t addr_size = section.getAddressSize(); + llvm::DataExtractor::Cursor cursor(0); + while (cursor && cursor.tell() < section_size) { + while (cursor && cursor.tell() < section_size) { + // Skip over 0 padding. + if (section.getU8(cursor) == 0) + continue; + cursor.seek(cursor.tell() - 1); + break; + } + uint64_t version = section.getULEB128(cursor); + uint64_t record_size = section.getULEB128(cursor); + if (version == 1) { + llvm::DataExtractor record(section.getData().drop_front(cursor.tell()), + le, addr_size); + llvm::DataExtractor::Cursor cursor(0); + uint64_t type_size = record.getULEB128(cursor); + llvm::StringRef type_name = record.getBytes(cursor, type_size); + llvm::Error error = cursor.takeError(); + if (!error) + fn(llvm::DataExtractor(record.getData().drop_front(cursor.tell()), le, + addr_size), + type_name); + else + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), std::move(error), + "{0}"); + } else { + // Skip unsupported record. + LLDB_LOG( + GetLog(LLDBLog::DataFormatters), + "Skipping unsupported embedded type summary of version {0} in {1}.", + version, module.GetFileSpec()); + } + section.skip(cursor, record_size); + } + if (!cursor) + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(), "{0}"); +} + +void LoadTypeSummariesForModule(ModuleSP module_sp) { + ForEachFormatterInModule( + *module_sp, eSectionTypeLLDBTypeSummaries, + [&](llvm::DataExtractor extractor, llvm::StringRef type_name) { + TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString("default"), + category); + // The type summary record is serialized as follows. + // + // * The size of the summary string + // * The summary string + // + // Integers are encoded using ULEB. + llvm::DataExtractor::Cursor cursor(0); + uint64_t summary_size = extractor.getULEB128(cursor); + llvm::StringRef summary_string = + extractor.getBytes(cursor, summary_size); + if (!cursor) { + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(), + "{0}"); + return; + } + if (type_name.empty() || summary_string.empty()) { + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Missing string(s) in embedded type summary in {0}, " + "type_name={1}, summary={2}", + module_sp->GetFileSpec(), type_name, summary_string); + return; + } + TypeSummaryImpl::Flags flags; + auto summary_sp = std::make_shared( + flags, summary_string.str().c_str()); + FormatterMatchType match_type = eFormatterMatchExact; + if (type_name.front() == '^') + match_type = eFormatterMatchRegex; + category->AddTypeSummary(type_name, match_type, summary_sp); + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Loaded embedded type summary for '{0}' from {1}.", type_name, + module_sp->GetFileSpec()); + }); +} + +void LoadFormattersForModule(ModuleSP module_sp) { + ForEachFormatterInModule( + *module_sp, eSectionTypeLLDBFormatters, + [&](llvm::DataExtractor extractor, llvm::StringRef type_name) { + // * Function signature (1 byte) + // * Length of the program (ULEB128) + // * The program bytecode + TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString("default"), + category); + llvm::DataExtractor::Cursor cursor(0); + uint64_t flags = extractor.getULEB128(cursor); + while (cursor && cursor.tell() < extractor.size()) { + uint8_t signature = extractor.getU8(cursor); + uint64_t size = extractor.getULEB128(cursor); + llvm::StringRef bytecode = extractor.getBytes(cursor, size); + if (!cursor) { + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(), + "{0}"); + return; + } + if (signature == 0) { + auto summary_sp = std::make_shared( + TypeSummaryImpl::Flags(flags), + llvm::MemoryBuffer::getMemBufferCopy(bytecode)); + FormatterMatchType match_type = eFormatterMatchExact; + if (type_name.front() == '^') + match_type = eFormatterMatchRegex; + category->AddTypeSummary(type_name, match_type, summary_sp); + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Loaded embedded type summary for '{0}' from {1}.", + type_name, module_sp->GetFileSpec()); + } else + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Unsupported formatter signature {0} for '{1}' in {2}", + signature, type_name, module_sp->GetFileSpec()); + } + }); +} +} // namespace lldb_private diff --git a/lldb/source/DataFormatters/FormattersHelpers.cpp b/lldb/source/DataFormatters/FormattersHelpers.cpp index 085ed3d0a2f29..123b29a47a272 100644 --- a/lldb/source/DataFormatters/FormattersHelpers.cpp +++ b/lldb/source/DataFormatters/FormattersHelpers.cpp @@ -21,6 +21,308 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +/* TO_UPSTREAM(BoundsSafety) ON */ + +/// Describes to what extent a pointer is out-of-bounds. +enum OOBKind { + ///< Partially overlapping valid address range. + Partial = 0, + + ///< Out-of-bounds without overlapping valid address range. + Full, + + ///< Out-of-bounds which would overflow address space. + Overflow, +}; + +struct OOBInfo { + ///< Exclusive upper bound of OOB access. + lldb::addr_t upper_bound; + + ///< What kind of OOB access this was. + OOBKind kind; +}; + +static char const *GetOOBKindString(OOBKind kind) { + switch (kind) { + case OOBKind::Full: + return "out-of-bounds"; + case OOBKind::Partial: + return "partially out-of-bounds"; + case OOBKind::Overflow: + return "overflown bounds"; + } +} + +/// Determines whether \ref ptr (with pointee size of +/// \ref elt_size) would cause an out-of-bounds access +/// to the range [lower_bound, upper_bound). If an OOB +/// access would occur, returns OOB information, otherwise +/// returns std::nullopt. +/// +/// \param[in] ptr Inclusive lower bound of pointer. +/// \param[in] upper_bound Exclusive upper bound of the +/// address range that \ref ptr +/// is trying to access. +/// \param[in] lower_bound Inclusive lower bound of the +/// address range that \ref ptr +/// is trying to access. +/// \param[in] elt_size Size of elements pointed to by \ref ptr. +/// If 0, this function assumes the minimum +/// bytes read by an access is 1 byte. +/// +/// \returns OOBInfo describing the out-of-bounds of \ref ptr. +/// If the access was in-bounds, returns std::nullopt. +static std::optional GetOOBInfo(lldb::addr_t ptr, + lldb::addr_t upper_bound, + lldb::addr_t lower_bound, + uint64_t elt_size) { + if (elt_size == 0) { + // Note when `elt_size == 0` we effectively do `ptr +1 > upper_bound`, + // i.e. we assume that we read a single byte which is the minimum amount + // of memory that could be accessed. This is a "best effort" attempt to + // report OOB when we don't know the type. + elt_size = 1; + } + + lldb::addr_t ptr_upper_bound = ptr + elt_size; + // We consider an access that would overflow the address space also + // out-of-bounds. + if (ptr_upper_bound < ptr) + return OOBInfo{LLDB_INVALID_ADDRESS, OOBKind::Overflow}; + + // Note `ptr_upper_bound == upper_bound` is ok because that corresponds + // to accessing the last element of the buffer. + + // bounds: lo---hi + // ptr: lo--hi + // + // or, + // + // bounds: lo---hi + // ptr: lo--hi <<< fully OOB since upper-bound is exclusive + // + if (ptr_upper_bound <= lower_bound) + return OOBInfo{ptr_upper_bound, OOBKind::Full}; + + // bounds: lo--hi + // ptr: lo--hi + // + // or, + // + // bounds: lo--hi + // ptr: lo--hi + // + if (ptr >= upper_bound) + return OOBInfo{ptr_upper_bound, OOBKind::Full}; + + // bounds: lo--hi + // ptr: lo--hi + // + // or, + // + // bounds: lo--hi + // ptr: lo----hi + // + if (ptr < lower_bound && ptr_upper_bound > lower_bound && + ptr_upper_bound <= upper_bound) + return OOBInfo{ptr_upper_bound, OOBKind::Partial}; + + // bounds: lo--hi + // ptr: lo--hi + // + // or, + // + // bounds: lo--hi + // ptr: lo----hi + // + if (ptr >= lower_bound && ptr < upper_bound && ptr_upper_bound > upper_bound) + return OOBInfo{ptr_upper_bound, OOBKind::Partial}; + + // bounds: lo--hi + // ptr: lo------hi + // + if (ptr < lower_bound && ptr_upper_bound > upper_bound) + return OOBInfo{ptr_upper_bound, OOBKind::Partial}; + + // Otherwise, in-bounds + return std::nullopt; +} + +/// Helper function to read a single pointer out of valobj. +static std::optional ReadPtr(ValueObject &valobj) { + // Read a pointer out of valobj + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return std::nullopt; + uint32_t address_size = process_sp->GetAddressByteSize(); + + DataExtractor data; + data.SetAddressByteSize(address_size); + Status error; + uint32_t BytesRead = valobj.GetData(data, error); + if (!error.Success() || BytesRead != address_size) + return std::nullopt; + + offset_t data_offset = 0; + return data.GetAddress(&data_offset); +} + +bool lldb_private::formatters::FormatBoundsSafetyAttrPointer( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + // These types are named using the pattern: + // __bounds_safety::attribute_name::attribute_expression + llvm::StringRef type_name = valobj.GetTypeName().GetStringRef(); + llvm::SmallVector splits; + type_name.split(splits, "::"); + + if (splits.size() != 3 || splits.front() != "__bounds_safety") + return false; + + // We need these to be null terminated for formatted print. + std::string attribute_name = splits[1].str(); + std::string attribute_expr = splits[2].str(); + + int addrsize = 12; + if (auto target_sp = valobj.GetTargetSP()) + addrsize = target_sp->GetArchitecture().GetAddressByteSize() == 4 ? 8 : 12; + + std::optional maybe_ptr = ReadPtr(valobj); + if (!maybe_ptr) + return false; + + stream.Printf("(ptr: 0x%*.*" PRIx64 " %s: %s)", addrsize, addrsize, + *maybe_ptr, attribute_name.c_str(), attribute_expr.c_str()); + return true; +} + +bool lldb_private::formatters::FormatBoundsSafetyDynamicRangeAttrPointer( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + // These types are named using the pattern: + // __bounds_safety::dynamic_range::start_expr::end_expr + llvm::StringRef type_name = valobj.GetTypeName().GetStringRef(); + llvm::SmallVector splits; + type_name.split(splits, "::"); + + if (splits.size() != 4 || splits[0] != "__bounds_safety" || + splits[1] != "dynamic_range") { + auto str = type_name.str(); + stream.Printf("%s", str.c_str()); + return true; + } + + std::string start_expr = splits[2].str(); + std::string end_expr = splits[3].str(); + + int addrsize = 12; + if (auto target_sp = valobj.GetTargetSP()) + addrsize = target_sp->GetArchitecture().GetAddressByteSize() == 4 ? 8 : 12; + + std::optional maybe_ptr = ReadPtr(valobj); + if (!maybe_ptr) + return false; + + stream.Printf("(ptr: 0x%*.*" PRIx64, addrsize, addrsize, *maybe_ptr); + if (!start_expr.empty()) + stream.Printf(" start_expr: %s", start_expr.c_str()); + if (!end_expr.empty()) + stream.Printf(" end_expr: %s", end_expr.c_str()); + stream.Printf(")"); + return true; +} + +bool lldb_private::formatters::FormatBoundsSafetyPointer( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + CompilerType compiler_type = valobj.GetCompilerType(); + if (!compiler_type) + return false; + + const bool is_bidi_indexable = compiler_type.IsBoundsSafetyBidiIndexable(); + const bool is_indexable = compiler_type.IsBoundsSafetyIndexable(); + if (!is_indexable && !is_bidi_indexable) + return false; + + // BoundsSafety pointers are wide pointers e.g. + // + // struct wide_ptr { + // T *raw_ptr; + // T *upper_bound; // Exists in both indexable and bidi indexable case + // T *lower_bound; // Exists only for bidi indexable case + // }; + // + // We want to extract the upper bound, and lower bound if it exists. + + uint32_t address_size = process_sp->GetAddressByteSize(); + uint32_t expected_data_size = + is_bidi_indexable ? 3 * address_size : 2 * address_size; + + DataExtractor data; + data.SetAddressByteSize(address_size); + Status error; + uint32_t BytesRead = valobj.GetData(data, error); + if (!error.Success() || BytesRead != expected_data_size) + return false; + + offset_t data_offset = 0; + auto read_next_pointer = [&data, &data_offset]() { + return addr_t(data.GetAddress(&data_offset)); + }; + + addr_t ptr = read_next_pointer(); + addr_t upper_bound = read_next_pointer(); + addr_t lower_bound = is_bidi_indexable ? read_next_pointer() : ptr; + + int addrsize = 12; + if (auto target_sp = valobj.GetTargetSP()) + addrsize = target_sp->GetArchitecture().GetAddressByteSize() == 4 ? 8 : 12; + + // Compute size of pointee if it is known. + CompilerType pointee_type; + uint64_t pointee_byte_size = 0; + if (compiler_type.IsPointerType(&pointee_type)) { + pointee_byte_size = + pointee_type.GetByteSize(valobj.GetTargetSP().get()).value_or(0); + } + + if (auto maybe_oob_info = GetOOBInfo(ptr, /*upper_bound=*/upper_bound, + /*lower_bound=*/lower_bound, + /*elt_size=*/pointee_byte_size)) { + // For OOB accesses, we print ptr's upper bound. + stream.Printf("(%s ptr: 0x%*.*" PRIx64 "..0x%*.*" PRIx64, + GetOOBKindString(maybe_oob_info->kind), addrsize, addrsize, + ptr, addrsize, addrsize, maybe_oob_info->upper_bound); + } else { + stream.Printf("(ptr: 0x%*.*" PRIx64, addrsize, addrsize, ptr); + } + + if (is_bidi_indexable) { + stream.Printf(", bounds: 0x%*.*" PRIx64 "..0x%*.*" PRIx64 ")", addrsize, + addrsize, lower_bound, addrsize, addrsize, upper_bound); + } else { + stream.Printf(", upper bound: 0x%*.*" PRIx64 ")", addrsize, addrsize, + upper_bound); + } + + // After we apply the BoundsSafety formatting for the pointer if the base type + // e.g. char* has a summary we need to obtain that summary and append it. + + // We need to strip the BoundsSafety attributes and create a new ValueObject + // for that stripped type. + compiler_type = compiler_type.AddBoundsSafetyUnspecifiedAttribute(); + lldb::ValueObjectSP bounds_safety_attributes_removed = valobj.Cast(compiler_type); + + const char *base_summary = bounds_safety_attributes_removed->GetSummaryAsCString(); + if (base_summary) + stream.Printf(" %s", base_summary); + + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void lldb_private::formatters::AddFormat( TypeCategoryImpl::SharedPointer category_sp, lldb::Format format, llvm::StringRef type_name, TypeFormatImpl::Flags flags, bool regex) { diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp index b8f2bad1e6d2a..2c863b364538f 100644 --- a/lldb/source/DataFormatters/TypeSummary.cpp +++ b/lldb/source/DataFormatters/TypeSummary.cpp @@ -8,9 +8,7 @@ #include "lldb/DataFormatters/TypeSummary.h" - - - +#include "FormatterBytecode.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-public.h" @@ -48,6 +46,21 @@ TypeSummaryOptions::SetCapping(lldb::TypeSummaryCapping cap) { TypeSummaryImpl::TypeSummaryImpl(Kind kind, const TypeSummaryImpl::Flags &flags) : m_flags(flags), m_kind(kind) {} +std::string TypeSummaryImpl::GetSummaryKindName() { + switch (m_kind) { + case Kind::eSummaryString: + return "string"; + case Kind::eCallback: + return "callback"; + case Kind::eScript: + return "python"; + case Kind::eInternal: + return "c++"; + case Kind::eBytecode: + return "bytecode"; + } +} + StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *format_cstr) : TypeSummaryImpl(Kind::eSummaryString, flags), m_format_str() { @@ -116,6 +129,8 @@ std::string StringSummaryFormat::GetDescription() { return std::string(sstr.GetString()); } +std::string StringSummaryFormat::GetName() { return m_format_str; } + CXXFunctionSummaryFormat::CXXFunctionSummaryFormat( const TypeSummaryImpl::Flags &flags, Callback impl, const char *description) : TypeSummaryImpl(Kind::eCallback, flags), m_impl(impl), @@ -145,15 +160,27 @@ std::string CXXFunctionSummaryFormat::GetDescription() { return std::string(sstr.GetString()); } +std::string CXXFunctionSummaryFormat::GetName() { return m_description; } + ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *function_name, const char *python_script) : TypeSummaryImpl(Kind::eScript, flags), m_function_name(), m_python_script(), m_script_function_sp() { - if (function_name) + // Take preference in the python script name over the function name. + if (function_name) { m_function_name.assign(function_name); - if (python_script) + m_script_formatter_name = function_name; + } + if (python_script) { m_python_script.assign(python_script); + m_script_formatter_name = python_script; + } + + // Python scripts include the tabbing of the function def so we remove the + // leading spaces. + m_script_formatter_name = m_script_formatter_name.erase( + 0, m_script_formatter_name.find_first_not_of(' ')); } bool ScriptSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, @@ -201,3 +228,76 @@ std::string ScriptSummaryFormat::GetDescription() { } return std::string(sstr.GetString()); } + +std::string ScriptSummaryFormat::GetName() { return m_script_formatter_name; } + +BytecodeSummaryFormat::BytecodeSummaryFormat( + const TypeSummaryImpl::Flags &flags, + std::unique_ptr bytecode) + : TypeSummaryImpl(Kind::eBytecode, flags), m_bytecode(std::move(bytecode)) { +} + +bool BytecodeSummaryFormat::FormatObject(ValueObject *valobj, + std::string &retval, + const TypeSummaryOptions &options) { + if (!valobj) + return false; + + TargetSP target_sp(valobj->GetTargetSP()); + + if (!target_sp) { + retval.assign("error: no target"); + return false; + } + + std::vector control( + {m_bytecode->getBuffer()}); + FormatterBytecode::DataStack data({valobj->GetSP()}); + llvm::Error error = FormatterBytecode::Interpret( + control, data, FormatterBytecode::sel_summary); + if (error) { + retval = llvm::toString(std::move(error)); + return false; + } + if (!data.size()) { + retval = "empty stack"; + return false; + } + auto &top = data.back(); + retval = ""; + llvm::raw_string_ostream os(retval); + if (auto s = std::get_if(&top)) + os << *s; + else if (auto u = std::get_if(&top)) + os << *u; + else if (auto i = std::get_if(&top)) + os << *i; + else if (auto valobj = std::get_if(&top)) { + if (!valobj->get()) + os << "empty object"; + else + os << valobj->get()->GetValueAsCString(); + } else if (auto type = std::get_if(&top)) { + os << type->TypeDescription(); + } else if (auto sel = std::get_if(&top)) { + os << toString(*sel); + } + return true; +} + +std::string BytecodeSummaryFormat::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s%s%s%s%s\n ", Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : ""); + // FIXME: sstr.PutCString(disassembly); + return std::string(sstr.GetString()); +} + +std::string BytecodeSummaryFormat::GetName() { + return "LLDB bytecode formatter"; +} diff --git a/lldb/source/Expression/FunctionCaller.cpp b/lldb/source/Expression/FunctionCaller.cpp index 67f9cd5758be2..ddf1e1151bdcf 100644 --- a/lldb/source/Expression/FunctionCaller.cpp +++ b/lldb/source/Expression/FunctionCaller.cpp @@ -8,6 +8,7 @@ #include "lldb/Expression/FunctionCaller.h" #include "lldb/Core/Module.h" +#include "lldb/Core/Progress.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -338,6 +339,10 @@ lldb::ExpressionResults FunctionCaller::ExecuteFunction( DiagnosticManager &diagnostic_manager, Value &results) { lldb::ExpressionResults return_value = lldb::eExpressionSetupError; + Debugger *debugger = + exe_ctx.GetTargetPtr() ? &exe_ctx.GetTargetPtr()->GetDebugger() : nullptr; + Progress progress("Calling function", FunctionName(), {}, debugger); + // FunctionCaller::ExecuteFunction execution is always just to get the // result. Unless explicitly asked for, ignore breakpoints and unwind on // error. diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp index 2dfea8a53e5f2..887c98d430515 100644 --- a/lldb/source/Expression/UserExpression.cpp +++ b/lldb/source/Expression/UserExpression.cpp @@ -14,6 +14,7 @@ #include #include "lldb/Core/Module.h" +#include "lldb/Core/Progress.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Expression/IRExecutionUnit.h" @@ -431,6 +432,18 @@ UserExpression::Execute(DiagnosticManager &diagnostic_manager, const EvaluateExpressionOptions &options, lldb::UserExpressionSP &shared_ptr_to_me, lldb::ExpressionVariableSP &result_var) { + Debugger *debugger = + exe_ctx.GetTargetPtr() ? &exe_ctx.GetTargetPtr()->GetDebugger() : nullptr; + std::string details; + if (m_options.IsForUtilityExpr()) + details = "LLDB utility"; + else if (m_expr_text.size() > 15) + details = m_expr_text.substr(0, 14) + "…"; + else + details = m_expr_text; + + Progress progress("Running expression", details, {}, debugger); + lldb::ExpressionResults expr_result = DoExecute( diagnostic_manager, exe_ctx, options, shared_ptr_to_me, result_var); Target *target = exe_ctx.GetTargetPtr(); diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp index dad3b386d14a6..b4644ad9c6e49 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp @@ -13,6 +13,7 @@ #include "SwiftExpressionParser.h" #include "Plugins/Language/Swift/LogChannelSwift.h" +#include "Plugins/TypeSystem/Swift/SwiftASTContext.h" #include "SwiftASTManipulator.h" #include "SwiftDiagnostic.h" #include "SwiftExpressionSourceCode.h" @@ -1195,8 +1196,9 @@ AddArchetypeTypeAliases(std::unique_ptr &code_manipulator, llvm::StringRef &type_name = pair.getFirst(); MetadataPointerInfo &info = pair.getSecond(); - auto dependent_type = - typeref_typesystem->CreateGenericTypeParamType(info.depth, info.index); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(type_name); + auto dependent_type = typeref_typesystem->CreateGenericTypeParamType( + info.depth, info.index, flavor); auto bound_type = runtime->BindGenericTypeParameters(stack_frame, dependent_type); if (!bound_type) { @@ -1312,19 +1314,21 @@ SwiftExpressionParser::ParseAndImport( return make_error(); auto module_id = ast_context->getIdentifier(expr_name_buf); - module = swift::ModuleDecl::create(module_id, **ast_context, importInfo); - - swift::SourceFileKind source_file_kind = swift::SourceFileKind::Library; - if (playground || repl) { - source_file_kind = swift::SourceFileKind::Main; - } + module = swift::ModuleDecl::create( + module_id, **ast_context, importInfo, + [&](swift::ModuleDecl *module, auto addFile) { + swift::SourceFileKind source_file_kind = swift::SourceFileKind::Library; + if (playground || repl) { + source_file_kind = swift::SourceFileKind::Main; + } - // Create the source file. Note, we disable delayed parsing for the - // swift expression parser. - source_file = new (**ast_context) swift::SourceFile( - *module, source_file_kind, buffer_id, - swift::SourceFile::ParsingFlags::DisableDelayedBodies); - module->addFile(*source_file); + // Create the source file. Note, we disable delayed parsing for the + // swift expression parser. + source_file = new (**ast_context) swift::SourceFile( + *module, source_file_kind, buffer_id, + swift::SourceFile::ParsingFlags::DisableDelayedBodies); + addFile(source_file); + }); } // Swift Modules that rely on shared libraries (not frameworks) // don't record the link information in the swiftmodule file, so we @@ -1504,10 +1508,11 @@ bool SwiftExpressionParser::Complete(CompletionRequest &request, unsigned line, /// system. static bool RedirectCallFromSinkToTrampolineFunction(llvm::Module &module, - SwiftASTManipulator &manipulator) { + SwiftASTManipulator &manipulator, + swift::ASTContext &ast_ctx) { Log *log = GetLog(LLDBLog::Expressions); - swift::Mangle::ASTMangler mangler; + swift::Mangle::ASTMangler mangler(ast_ctx); auto *entrypoint_decl = manipulator.GetEntrypointDecl(); if (!entrypoint_decl) { LLDB_LOG(log, "[RedirectCallFromSinkToTrampolineFunction] Could not set " @@ -2036,8 +2041,9 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager, llvm::ArrayRef()); if (GenModule) { - swift::performLLVMOptimizations(IRGenOpts, GenModule.getModule(), - GenModule.getTargetMachine(), nullptr); + swift::performLLVMOptimizations( + IRGenOpts, m_swift_ast_ctx.GetDiagnosticEngine(), nullptr, + GenModule.getModule(), GenModule.getTargetMachine(), nullptr); } auto ContextAndModule = std::move(GenModule).release(); m_llvm_context.reset(ContextAndModule.first); @@ -2084,16 +2090,18 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager, LLDB_LOG(log, "Generated IR module:\n{0}", s); } - if (m_options.GetBindGenericTypes() == lldb::eDontBind && - !RedirectCallFromSinkToTrampolineFunction( - *m_module.get(), *parsed_expr->code_manipulator.get())) { - diagnostic_manager.Printf( - eSeverityError, - "couldn't setup call to the trampoline function. Please enable the " - "expression log by running \"log enable lldb " - "expr\", then run the failing expression again, and file a " - "bugreport with the log output."); - return ParseResult::unrecoverable_error; + if (ThreadSafeASTContext ast_ctx = m_swift_ast_ctx.GetASTContext()) { + if (m_options.GetBindGenericTypes() == lldb::eDontBind && + !RedirectCallFromSinkToTrampolineFunction( + *m_module.get(), *parsed_expr->code_manipulator.get(), **ast_ctx)) { + diagnostic_manager.Printf( + eSeverityError, + "couldn't setup call to the trampoline function. Please enable the " + "expression log by running \"log enable lldb " + "expr\", then run the failing expression again, and file a " + "bugreport with the log output."); + return ParseResult::unrecoverable_error; + } } if (log) { diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp index a798a7f143fdb..fa215b21afd01 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SwiftExpressionSourceCode.h" +#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h" #include "SwiftPersistentExpressionState.h" #include "Plugins/ExpressionParser/Swift/SwiftASTManipulator.h" @@ -60,10 +61,14 @@ static llvm::Expected TransformPackType( if (!ts) return llvm::createStringError(llvm::errc::not_supported, "no typeref typesystem"); + + auto mangled_name = type.GetMangledTypeName().GetStringRef(); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + using namespace swift::Demangle; Demangler dem; - NodePointer node = ts->GetCanonicalDemangleTree( - dem, type.GetMangledTypeName().GetStringRef()); + + NodePointer node = ts->GetCanonicalDemangleTree(dem, mangled_name); node = TypeSystemSwiftTypeRef::Transform(dem, node, [](NodePointer n) { if (n->getKind() == Node::Kind::SILPackIndirect && @@ -76,7 +81,8 @@ static llvm::Expected TransformPackType( }); bool error = false; - ConstString type_name = ts->RemangleAsType(dem, node).GetMangledTypeName(); + ConstString type_name = + ts->RemangleAsType(dem, node, flavor).GetMangledTypeName(); swift::Demangle::DemangleOptions options; options = swift::Demangle::DemangleOptions::SimplifiedUIDemangleOptions(); options.DisplayStdlibModule = false; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftREPL.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftREPL.cpp index b7ca575e830c9..53e8df708720b 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftREPL.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftREPL.cpp @@ -607,18 +607,22 @@ void SwiftREPL::CompleteCode(const std::string ¤t_code, if (!repl_module) { swift::ImplicitImportInfo importInfo; importInfo.StdlibKind = swift::ImplicitStdlibKind::Stdlib; + auto repl_module_or_err = swift_ast->CreateModule( - completion_module_info.path.back().GetString(), importInfo); + completion_module_info.path.back().GetString(), importInfo, + [&](swift::ModuleDecl *repl_module, auto addFile) { + auto bufferID = (*ast)->SourceMgr.addMemBufferCopy("// swift repl\n"); + swift::SourceFile *repl_source_file = new (**ast) swift::SourceFile( + *repl_module, swift::SourceFileKind::Main, bufferID); + addFile(repl_source_file); + }); if (!repl_module_or_err) { llvm::consumeError(repl_module_or_err.takeError()); return; } repl_module = &*repl_module_or_err; - auto bufferID = (*ast)->SourceMgr.addMemBufferCopy("// swift repl\n"); - swift::SourceFile *repl_source_file = new (**ast) - swift::SourceFile(*repl_module, swift::SourceFileKind::Main, bufferID); - repl_module->addFile(*repl_source_file); - swift::performImportResolution(*repl_source_file); + + swift::performImportResolution(repl_module); m_completion_module_initialized = true; } if (repl_module) { diff --git a/lldb/source/Plugins/Language/Swift/SwiftOptionSet.h b/lldb/source/Plugins/Language/Swift/SwiftOptionSet.h index 3516d0890b53b..cbf9de514fc7f 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftOptionSet.h +++ b/lldb/source/Plugins/Language/Swift/SwiftOptionSet.h @@ -35,6 +35,7 @@ struct SwiftOptionSetSummaryProvider : public TypeSummaryImpl { const TypeSummaryOptions &options) override; std::string GetDescription() override; bool DoesPrintChildren(ValueObject *valobj) const override; + std::string GetName() override { return "SwiftOptionSetSummaryProvider"; } private: SwiftOptionSetSummaryProvider(const SwiftOptionSetSummaryProvider &) = delete; diff --git a/lldb/source/Plugins/Language/Swift/SwiftOptional.h b/lldb/source/Plugins/Language/Swift/SwiftOptional.h index 2f648cffc79a7..a2af910a79312 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftOptional.h +++ b/lldb/source/Plugins/Language/Swift/SwiftOptional.h @@ -69,6 +69,7 @@ struct SwiftOptionalSummaryProvider : public TypeSummaryImpl { std::string GetDescription() override; bool DoesPrintChildren(ValueObject *valobj) const override; bool DoesPrintValue(ValueObject *valobj) const override; + std::string GetName() override { return "SwiftOptionalSummaryProvider"; } private: SwiftOptionalSummaryProvider(const SwiftOptionalSummaryProvider &) = delete; diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/Swift/CMakeLists.txt index 0318e5c3fbfd0..a6c0c08f263e6 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/CMakeLists.txt +++ b/lldb/source/Plugins/LanguageRuntime/Swift/CMakeLists.txt @@ -6,6 +6,7 @@ add_lldb_library(lldbPluginSwiftLanguageRuntime PLUGIN SwiftLanguageRuntimeNames.cpp SwiftLanguageRuntimeRemoteAST.cpp SwiftMetadataCache.cpp + SwiftTask.cpp LINK_LIBS swiftAST diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp index fe8c4b75535b3..53aa8d51de476 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp @@ -401,6 +401,8 @@ class TargetReflectionContext : public ReflectionContextInterface { result.hasIsRunning = task_info.HasIsRunning; result.isRunning = task_info.IsRunning; result.isEnqueued = task_info.IsEnqueued; + result.id = task_info.Id; + result.resumeAsyncContext = task_info.ResumeAsyncContext; return result; } diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h index 8dc0e10716169..0e449c13d961d 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h @@ -15,6 +15,7 @@ #include +#include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "swift/ABI/ObjectFile.h" #include "swift/Remote/RemoteAddress.h" @@ -163,6 +164,8 @@ class ReflectionContextInterface { bool hasIsRunning = false; bool isRunning = false; bool isEnqueued = false; + uint64_t id = 0; + lldb::addr_t resumeAsyncContext = LLDB_INVALID_ADDRESS; }; // The default limits are copied from swift-inspect. virtual llvm::Expected diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp index fc976507b6f1e..0ca4b03edc2a2 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp @@ -16,6 +16,7 @@ #include "SwiftMetadataCache.h" #include "Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.h" +#include "Plugins/LanguageRuntime/Swift/SwiftTask.h" #include "Plugins/Process/Utility/RegisterContext_x86.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "Plugins/TypeSystem/Swift/SwiftDemangle.h" @@ -1257,8 +1258,7 @@ void SwiftLanguageRuntime::RegisterGlobalError(Target &target, ConstString name, module_info.path.push_back(ConstString(module_name)); swift::ModuleDecl *module_decl = nullptr; - auto module_decl_or_err = swift_ast_ctx->CreateModule(module_name, - /*importInfo*/ {}); + auto module_decl_or_err = swift_ast_ctx->CreateEmptyModule(module_name); if (!module_decl_or_err) llvm::consumeError(module_decl_or_err.takeError()); else @@ -1289,11 +1289,14 @@ void SwiftLanguageRuntime::RegisterGlobalError(Target &target, ConstString name, ConstString mangled_name; - { - swift::Mangle::ASTMangler mangler(true); + if (ThreadSafeASTContext ast_ctx = swift_ast_ctx->GetASTContext()) { + swift::Mangle::ASTMangler mangler(**ast_ctx, true); mangled_name = ConstString(mangler.mangleGlobalVariableFull(var_decl)); } + if (mangled_name.IsEmpty()) + return; + lldb::addr_t symbol_addr; { @@ -2083,6 +2086,116 @@ class CommandObjectSwift_RefCount : public CommandObjectRaw { } }; +/// Construct a `ThreadTask` instance for a Task variable contained in the first +/// argument. +static llvm::Expected +ThreadForTaskVariable(Args &command, ExecutionContext &exe_ctx) { + if (!exe_ctx.GetFramePtr()) + return llvm::createStringError("no active frame selected"); + + if (command.empty() || command[0].ref().empty()) + return llvm::createStringError("missing task variable argument"); + + StackFrame &frame = exe_ctx.GetFrameRef(); + uint32_t path_options = + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + VariableSP var_sp; + Status status; + ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath( + command[0].c_str(), eDynamicDontRunTarget, path_options, var_sp, status); + if (!valobj_sp) + return status.takeError(); + + addr_t task_ptr = LLDB_INVALID_ADDRESS; + ThreadSafeReflectionContext reflection_ctx; + if (ValueObjectSP task_obj_sp = valobj_sp->GetChildMemberWithName("_task")) { + task_ptr = task_obj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (task_ptr != LLDB_INVALID_ADDRESS) + if (auto *runtime = SwiftLanguageRuntime::Get(exe_ctx.GetProcessSP())) + reflection_ctx = runtime->GetReflectionContext(); + } + if (task_ptr == LLDB_INVALID_ADDRESS || !reflection_ctx) + return llvm::createStringError("failed to access Task data from runtime"); + + llvm::Expected task_info = + reflection_ctx->asyncTaskInfo(task_ptr); + if (auto error = task_info.takeError()) + return error; + + return ThreadTask::Create(task_info->id, task_info->resumeAsyncContext, + exe_ctx); +} + +class CommandObjectLanguageSwiftTaskBacktrace final + : public CommandObjectParsed { +public: + CommandObjectLanguageSwiftTaskBacktrace(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "backtrace", + "Show the backtrace of Swift tasks. See `thread " + "backtrace` for customizing backtrace output.", + "language swift task backtrace ") { + AddSimpleArgumentList(eArgTypeVarName); + } + +private: + void DoExecute(Args &command, CommandReturnObject &result) override { + llvm::Expected thread_task = + ThreadForTaskVariable(command, m_exe_ctx); + if (auto error = thread_task.takeError()) { + result.AppendError(toString(std::move(error))); + return; + } + + // GetStatus prints the backtrace. + thread_task.get()->GetStatus(result.GetOutputStream(), 0, UINT32_MAX, 0, + false, true); + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + } +}; + +class CommandObjectLanguageSwiftTaskSelect final : public CommandObjectParsed { +public: + CommandObjectLanguageSwiftTaskSelect(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "select", + "Change the currently selected thread to thread representation of " + "the given Swift Task. See `thread select`.", + "language swift task select ") { + AddSimpleArgumentList(eArgTypeVarName); + } + +private: + void DoExecute(Args &command, CommandReturnObject &result) override { + llvm::Expected thread_task = + ThreadForTaskVariable(command, m_exe_ctx); + if (auto error = thread_task.takeError()) { + result.AppendError(toString(std::move(error))); + return; + } + + auto &thread_list = m_exe_ctx.GetProcessRef().GetThreadList(); + thread_list.AddThread(thread_task.get()); + thread_list.SetSelectedThreadByID(thread_task.get()->GetID()); + + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + } +}; + +class CommandObjectLanguageSwiftTask final : public CommandObjectMultiword { +public: + CommandObjectLanguageSwiftTask(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "task", "Commands for inspecting Swift Tasks.", + "language swift task []") { + LoadSubCommand("backtrace", + CommandObjectSP(new CommandObjectLanguageSwiftTaskBacktrace( + interpreter))); + LoadSubCommand( + "select", + CommandObjectSP(new CommandObjectLanguageSwiftTaskSelect(interpreter))); + } +}; + class CommandObjectMultiwordSwift : public CommandObjectMultiword { public: CommandObjectMultiwordSwift(CommandInterpreter &interpreter) @@ -2094,6 +2207,8 @@ class CommandObjectMultiwordSwift : public CommandObjectMultiword { interpreter))); LoadSubCommand("refcount", CommandObjectSP(new CommandObjectSwift_RefCount( interpreter))); + LoadSubCommand("task", CommandObjectSP(new CommandObjectLanguageSwiftTask( + interpreter))); } virtual ~CommandObjectMultiwordSwift() {} diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h index 902d8f22b8c6b..9765d7c398cad 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h @@ -20,6 +20,7 @@ #include "lldb/Core/PluginInterface.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/lldb-private.h" +#include "swift/Demangling/ManglingFlavor.h" #include #include "llvm/ADT/StringSet.h" @@ -131,6 +132,12 @@ class SwiftLanguageRuntime : public LanguageRuntime { /// since some day we may want to support more than one swift variant. static bool IsSwiftMangledName(llvm::StringRef name); + static swift::Mangle::ManglingFlavor + GetManglingFlavor(llvm::StringRef mangledName) { + if (mangledName.starts_with("$e") || mangledName.starts_with("_$e")) + return swift::Mangle::ManglingFlavor::Embedded; + return swift::Mangle::ManglingFlavor::Default; + } enum class FuncletComparisonResult { NotBothFunclets, DifferentAsyncFunctions, @@ -270,6 +277,7 @@ class SwiftLanguageRuntime : public LanguageRuntime { /// context. static void GetGenericParameterNamesForFunction( const SymbolContext &sc, const ExecutionContext *exe_ctx, + swift::Mangle::ManglingFlavor flavor, llvm::DenseMap &dict); /// Invoke callback for each DependentGenericParamType. diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp index 7b53d21cb5b86..5d470c646815a 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp @@ -35,6 +35,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTWalker.h" #include "swift/Demangling/Demangle.h" +#include "swift/Demangling/ManglingFlavor.h" #include "swift/RemoteInspection/ReflectionContext.h" #include "swift/RemoteInspection/TypeRefBuilder.h" #include "swift/Strings.h" @@ -315,8 +316,11 @@ class LLDBTypeInfoProvider : public swift::remote::TypeInfoProvider { auto *node = dem.demangleSymbol(wrapped); if (!node) { // Try `mangledName` as plain ObjC class name. Ex: NSObject, NSView, etc. + // Since this looking up an ObjC type, the default mangling falvor should + // be used. auto maybeMangled = swift_demangle::MangleClass( - dem, swift::MANGLING_MODULE_OBJC, mangledName); + dem, swift::MANGLING_MODULE_OBJC, mangledName, + swift::Mangle::ManglingFlavor::Default); if (!maybeMangled.isSuccess()) { LLDB_LOG(GetLog(LLDBLog::Types), "[LLDBTypeInfoProvider] invalid mangled name: {0}", @@ -636,7 +640,9 @@ CompilerType GetWeakReferent(TypeSystemSwiftTypeRef &ts, CompilerType type) { if (!n || n->getKind() != Node::Kind::SugaredOptional || !n->hasChildren()) return {}; n = n->getFirstChild(); - return ts.RemangleAsType(dem, n); + return ts.RemangleAsType( + dem, n, + SwiftLanguageRuntime::GetManglingFlavor(type.GetMangledTypeName())); } CompilerType GetTypeFromTypeRef(TypeSystemSwiftTypeRef &ts, @@ -645,7 +651,8 @@ CompilerType GetTypeFromTypeRef(TypeSystemSwiftTypeRef &ts, return {}; swift::Demangle::Demangler dem; swift::Demangle::NodePointer node = type_ref->getDemangling(dem); - return ts.RemangleAsType(dem, node); + // TODO: the mangling flavor should come from the TypeRef. + return ts.RemangleAsType(dem, node, ts.GetManglingFlavor()); } struct ExistentialSyntheticChild { @@ -1561,8 +1568,9 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Pack( swift::Demangle::Demangler dem; - auto expand_pack_type = [&](ConstString mangled_pack_type, - bool indirect) -> swift::Demangle::NodePointer { + auto expand_pack_type = [&](ConstString mangled_pack_type, bool indirect, + swift::Mangle::ManglingFlavor flavor) + -> swift::Demangle::NodePointer { // Find pack_type in the pack_expansions. unsigned i = 0; SwiftLanguageRuntime::GenericSignature::PackExpansion *pack_expansion = @@ -1692,7 +1700,7 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Pack( auto bound_typeref = reflection_ctx->ApplySubstitutions( type_ref, substitutions, ts->GetDescriptorFinder()); swift::Demangle::NodePointer node = bound_typeref->getDemangling(dem); - CompilerType type = ts->RemangleAsType(dem, node); + CompilerType type = ts->RemangleAsType(dem, node, flavor); // Add the substituted type to the tuple. elements.push_back({{}, type}); @@ -1718,6 +1726,9 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Pack( auto node = dem_ctx.demangleSymbolAsNode( pack_type.GetMangledTypeName().GetStringRef()); + auto flavor = + SwiftLanguageRuntime::GetManglingFlavor(pack_type.GetMangledTypeName()); + // Expand all the pack types that appear in the incoming type, // either at the root level or as arguments of bound generic types. bool indirect = false; @@ -1733,10 +1744,10 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Pack( if (node->getNumChildren() != 1) return node; node = node->getChild(0); - CompilerType pack_type = ts->RemangleAsType(dem, node); + CompilerType pack_type = ts->RemangleAsType(dem, node, flavor); ConstString mangled_pack_type = pack_type.GetMangledTypeName(); LLDB_LOG(log, "decoded pack_expansion type: {0}", mangled_pack_type); - auto result = expand_pack_type(mangled_pack_type, indirect); + auto result = expand_pack_type(mangled_pack_type, indirect, flavor); if (!result) { LLDB_LOG(log, "failed to expand pack type: {0}", mangled_pack_type); return node; @@ -1744,7 +1755,7 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Pack( return result; }); - CompilerType expanded_type = ts->RemangleAsType(dem, transformed); + CompilerType expanded_type = ts->RemangleAsType(dem, transformed, flavor); pack_type_or_name.SetCompilerType(expanded_type); AddressType address_type; @@ -1819,7 +1830,8 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Class( // useful to users. if (IsPrivateNSClass(node)) return false; - class_type_or_name.SetCompilerType(ts->RemangleAsType(dem, node)); + class_type_or_name.SetCompilerType(ts->RemangleAsType( + dem, node, swift::Mangle::ManglingFlavor::Default)); found = true; return true; } @@ -1851,9 +1863,13 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Class( class_type.GetMangledTypeName(), instance_ptr); return false; } + + auto flavor = SwiftLanguageRuntime::GetManglingFlavor( + class_type.GetMangledTypeName()); + swift::Demangle::Demangler dem; swift::Demangle::NodePointer node = typeref->getDemangling(dem); - CompilerType dynamic_type = ts->RemangleAsType(dem, node); + CompilerType dynamic_type = ts->RemangleAsType(dem, node, flavor); LLDB_LOG(log, "dynamic type of instance_ptr {0:x} is {1}", instance_ptr, class_type.GetMangledTypeName()); class_type_or_name.SetCompilerType(dynamic_type); @@ -2021,7 +2037,10 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_Existential( return false; swift::Demangle::Demangler dem; swift::Demangle::NodePointer node = typeref->getDemangling(dem); - class_type_or_name.SetCompilerType(ts->RemangleAsType(dem, node)); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor( + existential_type.GetMangledTypeName()); + + class_type_or_name.SetCompilerType(ts->RemangleAsType(dem, node, flavor)); address.SetRawAddress(out_address.getAddressData()); #ifndef NDEBUG @@ -2086,7 +2105,10 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_ExistentialMetatype( meta->addChild(node, dem); wrapped->addChild(meta,dem); - meta_type = tr_ts->RemangleAsType(dem, wrapped); + auto flavor = + SwiftLanguageRuntime::GetManglingFlavor(meta_type.GetMangledTypeName()); + meta_type = + tss->GetTypeSystemSwiftTypeRef()->RemangleAsType(dem, wrapped, flavor); class_type_or_name.SetCompilerType(meta_type); address.SetRawAddress(ptr); return true; @@ -2112,7 +2134,9 @@ CompilerType SwiftLanguageRuntime::GetTypeFromMetadata(TypeSystemSwift &ts, using namespace swift::Demangle; Demangler dem; NodePointer node = type_ref->getDemangling(dem); - return tr_ts->RemangleAsType(dem, node); + // TODO: the mangling flavor should come from the TypeRef. + return ts.GetTypeSystemSwiftTypeRef()->RemangleAsType( + dem, node, ts.GetTypeSystemSwiftTypeRef()->GetManglingFlavor()); } std::optional @@ -2235,7 +2259,10 @@ CompilerType SwiftLanguageRuntime::BindGenericTypeParameters( type_ref, substitutions, tr_ts->GetDescriptorFinder()); NodePointer node = bound_type_ref->getDemangling(dem); - return tr_ts->RemangleAsType(dem, node); + return ts->GetTypeSystemSwiftTypeRef()->RemangleAsType( + dem, node, + SwiftLanguageRuntime::GetManglingFlavor( + unbound_type.GetMangledTypeName())); } CompilerType @@ -2277,10 +2304,12 @@ SwiftLanguageRuntime::BindGenericTypeParameters(StackFrame &stack_frame, return; substitutions.insert({{depth, index}, type_ref}); }); + auto flavor = + SwiftLanguageRuntime::GetManglingFlavor(mangled_name.GetStringRef()); // Nothing to do if there are no type parameters. auto get_canonical = [&]() { - auto mangling = mangleNode(canonical); + auto mangling = mangleNode(canonical, flavor); if (!mangling.isSuccess()) return CompilerType(); return ts.GetTypeFromMangledTypename(ConstString(mangling.result())); @@ -2314,7 +2343,7 @@ SwiftLanguageRuntime::BindGenericTypeParameters(StackFrame &stack_frame, "No scratch context available."); return ts.GetTypeFromMangledTypename(mangled_name); } - CompilerType bound_type = scratch_ctx->RemangleAsType(dem, node); + CompilerType bound_type = scratch_ctx->RemangleAsType(dem, node, flavor); LLDB_LOG(GetLog(LLDBLog::Expressions | LLDBLog::Types), "Bound {0} -> {1}.", mangled_name, bound_type.GetMangledTypeName()); return bound_type; @@ -2625,6 +2654,13 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_ClangType( std::string remangled; { + auto type = in_value.GetCompilerType(); + std::optional mangling_flavor = + SwiftLanguageRuntime::GetManglingFlavor( + type.GetMangledTypeName().GetStringRef()); + if (!mangling_flavor) + return false; + // Create a mangle tree for Swift.Optional<$module.$class> using namespace swift::Demangle; NodeFactory factory; @@ -2653,7 +2689,7 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress_ClangType( factory); cty->addChild(c, factory); - auto mangling = mangleNode(global); + auto mangling = mangleNode(global, *mangling_flavor); if (!mangling.isSuccess()) return false; remangled = mangling.result(); @@ -3033,6 +3069,7 @@ SwiftLanguageRuntime::ResolveTypeAlias(CompilerType alias) { if (!tr_ts) return llvm::createStringError("could not get typesystem"); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(alias.GetMangledTypeName()); // Extract the mangling of the alias type's parent type and its // generic substitutions if any. auto mangled = alias.GetMangledTypeName().GetStringRef(); @@ -3109,7 +3146,7 @@ SwiftLanguageRuntime::ResolveTypeAlias(CompilerType alias) { swift::reflection::GenericArgumentMap substitutions; unsigned idx = 0; for (auto &child : *subst_node) { - auto mangling = swift_demangle::GetMangledName(dem, child); + auto mangling = swift_demangle::GetMangledName(dem, child, flavor); if (!mangling.isSuccess()) continue; const auto *type_ref = reflection_ctx->GetTypeRefOrNull( diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp index d87e7611a05b6..442500c55a55b 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp @@ -27,6 +27,7 @@ #include "Plugins/Process/Utility/RegisterContext_x86.h" #include "Utility/ARM64_DWARF_Registers.h" +#include "swift/Demangling/ManglingFlavor.h" #include "llvm/ADT/SmallSet.h" using namespace lldb; @@ -621,6 +622,7 @@ bool SwiftLanguageRuntime::IsSwiftMangledName(llvm::StringRef name) { void SwiftLanguageRuntime::GetGenericParameterNamesForFunction( const SymbolContext &const_sc, const ExecutionContext *exe_ctx, + swift::Mangle::ManglingFlavor flavor, llvm::DenseMap &dict) { // This terrifying cast avoids having too many differences with llvm.org. SymbolContext &sc = const_cast(const_sc); @@ -680,7 +682,8 @@ void SwiftLanguageRuntime::GetGenericParameterNamesForFunction( llvm::dyn_cast_or_null(type_system_or_err->get()); if (!ts) break; - CompilerType generic_type = ts->CreateGenericTypeParamType(depth, index); + CompilerType generic_type = + ts->CreateGenericTypeParamType(depth, index, flavor); CompilerType bound_type = runtime->BindGenericTypeParameters(*frame, generic_type); type_name = bound_type.GetDisplayTypeName(); @@ -734,7 +737,9 @@ std::string SwiftLanguageRuntime::DemangleSymbolAsString( // Resolve generic parameters in the current function. options.GenericParameterName = [&](uint64_t depth, uint64_t index) { if (!did_init) { - GetGenericParameterNamesForFunction(*sc, exe_ctx, dict); + GetGenericParameterNamesForFunction( + *sc, exe_ctx, SwiftLanguageRuntime::GetManglingFlavor(symbol), + dict); did_init = true; } auto it = dict.find({depth, index}); @@ -1204,6 +1209,7 @@ SwiftLanguageRuntime::GetGenericSignature(StringRef function_name, GenericSignature signature; unsigned num_generic_params = 0; + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(function_name); // Walk to the function type. Context ctx; auto *node = SwiftLanguageRuntime::DemangleSymbolAsNode(function_name, ctx); @@ -1312,10 +1318,10 @@ SwiftLanguageRuntime::GetGenericSignature(StringRef function_name, // Store the various type packs. swift::Demangle::Demangler dem; - auto mangling = swift::Demangle::mangleNode(type_node); + auto mangling = swift::Demangle::mangleNode(type_node, flavor); if (mangling.isSuccess()) signature.pack_expansions.back().mangled_type = - ts.RemangleAsType(dem, type_node).GetMangledTypeName(); + ts.RemangleAsType(dem, type_node, flavor).GetMangledTypeName(); // Assuming that there are no nested pack_expansions. return false; diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeRemoteAST.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeRemoteAST.cpp index 33f6fbd1cdafa..484b4270e5020 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeRemoteAST.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeRemoteAST.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/TypeSystem/Swift/SwiftASTContext.h" #include "LLDBMemoryReader.h" #include "ReflectionContextInterface.h" #include "SwiftLanguageRuntime.h" @@ -297,9 +298,12 @@ CompilerType SwiftLanguageRuntime::BindGenericTypeParametersRemoteAST( if (target_swift_type->hasArchetype()) target_swift_type = target_swift_type->mapTypeOutOfContext().getPointer(); - // Replace opaque types with their underlying types when possible. - swift::Mangle::ASTMangler mangler(true); + ThreadSafeASTContext ast_ctx = swift_ast_ctx->GetASTContext(); + if (!ast_ctx) + return base_type; + // Replace opaque types with their underlying types when possible. + swift::Mangle::ASTMangler mangler(**ast_ctx, true); // Rewrite all dynamic self types to their static self types. target_swift_type = target_swift_type.transformRec([](swift::TypeBase *type) diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.cpp new file mode 100644 index 0000000000000..7b2bc41bc2ed6 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.cpp @@ -0,0 +1,74 @@ +#include "SwiftTask.h" +#include "SwiftLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace lldb; + +namespace lldb_private { + +ThreadTask::ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn, + ExecutionContext &exe_ctx) + : Thread(exe_ctx.GetProcessRef(), tid, true), + m_reg_info_sp(exe_ctx.GetFrameSP()->GetRegisterContext()), + m_async_ctx(async_ctx), m_resume_fn(resume_fn) {} + +RegisterContextSP lldb_private::ThreadTask::GetRegisterContext() { + if (!m_async_reg_ctx_sp) + m_async_reg_ctx_sp = std::make_shared( + *this, m_reg_info_sp, m_resume_fn, m_async_ctx); + return m_async_reg_ctx_sp; +} + +llvm::Expected> +ThreadTask::Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx) { + auto &process = exe_ctx.GetProcessRef(); + auto &target = exe_ctx.GetTargetRef(); + + // A simplified description of AsyncContext. See swift/Task/ABI.h + // struct AsyncContext { + // AsyncContext *Parent; // offset 0 + // TaskContinuationFunction *ResumeParent; // offset 8 + // }; + // The resume function is stored at `offsetof(AsyncContext, ResumeParent)`, + // which is the async context's base address plus the size of a pointer. + uint32_t ptr_size = target.GetArchitecture().GetAddressByteSize(); + addr_t resume_ptr = async_ctx + ptr_size; + Status status; + addr_t resume_fn = process.ReadPointerFromMemory(resume_ptr, status); + if (status.Fail() || resume_fn == LLDB_INVALID_ADDRESS) + return createStringError("failed to read task's resume function"); + + return std::make_shared(tid, async_ctx, resume_fn, exe_ctx); +} + +RegisterContextTask::RegisterContextTask(Thread &thread, + RegisterContextSP reg_info_sp, + addr_t resume_fn, addr_t async_ctx) + : RegisterContext(thread, 0), m_reg_info_sp(reg_info_sp), + m_async_ctx(async_ctx), m_resume_fn(resume_fn) { + auto &target = thread.GetProcess()->GetTarget(); + auto triple = target.GetArchitecture().GetTriple(); + if (auto regnums = GetAsyncUnwindRegisterNumbers(triple.getArch())) + m_async_ctx_regnum = regnums->async_ctx_regnum; +} + +bool RegisterContextTask::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + if (!reg_info) + return false; + + if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC) { + reg_value = m_resume_fn; + return true; + } + if (reg_info->kinds[eRegisterKindLLDB] == m_async_ctx_regnum) { + reg_value = m_async_ctx; + return true; + } + return false; +} + +} // namespace lldb_private diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.h b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.h new file mode 100644 index 0000000000000..5f57b3d5cde52 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftTask.h @@ -0,0 +1,97 @@ + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/lldb-forward.h" + +namespace lldb_private { + +using namespace lldb; + +/// Provides a subset of Thread operations for Swift Tasks. +/// +/// Currently, this supports backtraces of Tasks, and selecting frames in the +/// backtrace. Async frames make available the variables that are stored in the +/// Task's "async context" (instead of the stack). +/// +/// See `Task` and `UnsafeCurrentTask` +class ThreadTask final : public Thread { +public: + ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn, + ExecutionContext &exe_ctx); + + static llvm::Expected> + Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx); + + /// Returns a Task specific register context (RegisterContextTask). + RegisterContextSP GetRegisterContext() override; + + ~ThreadTask() override { DestroyThread(); } + + // No-op overrides. + void RefreshStateAfterStop() override {} + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override { + return {}; + } + bool CalculateStopInfo() override { return false; } + +private: + /// A register context that is the source of `RegisterInfo` data. + RegisterContextSP m_reg_info_sp; + /// Lazily initialized `RegisterContextTask`. + RegisterContextSP m_async_reg_ctx_sp; + /// The Task's async context. + addr_t m_async_ctx = LLDB_INVALID_ADDRESS; + /// The address of the async context's resume function. + addr_t m_resume_fn = LLDB_INVALID_ADDRESS; +}; + +/// A Swift Task specific register context. Supporting class for `ThreadTask`, +/// see its documentation for details. +class RegisterContextTask final : public RegisterContext { +public: + RegisterContextTask(Thread &thread, RegisterContextSP reg_info_sp, + addr_t resume_fn, addr_t async_ctx); + + /// RegisterContextTask supports readonly from only two (necessary) + /// registers. Namely, the pc and the async context registers. + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + // Pass through overrides. + size_t GetRegisterCount() override { + return m_reg_info_sp->GetRegisterCount(); + } + const RegisterInfo *GetRegisterInfoAtIndex(size_t idx) override { + return m_reg_info_sp->GetRegisterInfoAtIndex(idx); + } + size_t GetRegisterSetCount() override { + return m_reg_info_sp->GetRegisterSetCount(); + } + const RegisterSet *GetRegisterSet(size_t reg_set) override { + return m_reg_info_sp->GetRegisterSet(reg_set); + } + lldb::ByteOrder GetByteOrder() override { + return m_reg_info_sp->GetByteOrder(); + } + + // No-op overrides. + void InvalidateAllRegisters() override {} + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override { + return false; + } + +private: + /// A register context that is the source of `RegisterInfo` data. + RegisterContextSP m_reg_info_sp; + /// The architecture specific regnum (LLDB) which holds the async context. + uint32_t m_async_ctx_regnum = LLDB_INVALID_REGNUM; + /// The Task's async context. + RegisterValue m_async_ctx; + /// The address of the async context's resume function. + RegisterValue m_resume_fn; +}; + +} // namespace lldb_private diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index c5fdd1139fc81..2dc77dbbf4181 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -1708,6 +1708,7 @@ static SectionType GetSectionTypeFromName(llvm::StringRef Name) { .Case(".gosymtab", eSectionTypeGoSymtab) .Case(".text", eSectionTypeCode) .Case(".lldbsummaries", lldb::eSectionTypeLLDBTypeSummaries) + .Case(".lldbformatters", lldb::eSectionTypeLLDBFormatters) .Case(".swift_ast", eSectionTypeSwiftModules) .Default(eSectionTypeOther); } diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index a570ec23021dd..ff7c68b6573ed 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -1213,6 +1213,7 @@ AddressClass ObjectFileMachO::GetAddressClass(lldb::addr_t file_addr) { case eSectionTypeDWARFGNUDebugAltLink: case eSectionTypeCTF: case eSectionTypeLLDBTypeSummaries: + case eSectionTypeLLDBFormatters: case eSectionTypeSwiftModules: return AddressClass::eDebug; @@ -1491,6 +1492,7 @@ static lldb::SectionType GetSectionType(uint32_t flags, static ConstString g_sect_name_go_symtab("__gosymtab"); static ConstString g_sect_name_ctf("__ctf"); static ConstString g_sect_name_lldb_summaries("__lldbsummaries"); + static ConstString g_sect_name_lldb_formatters("__lldbformatters"); static ConstString g_sect_name_swift_ast("__swift_ast"); if (section_name == g_sect_name_dwarf_debug_abbrev) @@ -1573,6 +1575,8 @@ static lldb::SectionType GetSectionType(uint32_t flags, return eSectionTypeCTF; if (section_name == g_sect_name_lldb_summaries) return lldb::eSectionTypeLLDBTypeSummaries; + if (section_name == g_sect_name_lldb_formatters) + return lldb::eSectionTypeLLDBFormatters; if (section_name == g_sect_name_swift_ast) return eSectionTypeSwiftModules; if (section_name == g_sect_name_objc_data || diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index ff91104c1cd37..ca4d4441f9fd7 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -1010,6 +1010,7 @@ SectionType ObjectFilePECOFF::GetSectionType(llvm::StringRef sect_name, .Cases(".eh_frame", ".eh_fram", eSectionTypeEHFrame) .Case(".gosymtab", eSectionTypeGoSymtab) .Case(".lldbsummaries", lldb::eSectionTypeLLDBTypeSummaries) + .Case(".lldbformatters", lldb::eSectionTypeLLDBFormatters) .Case("swiftast", eSectionTypeSwiftModules) .Default(eSectionTypeInvalid); if (section_type != eSectionTypeInvalid) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp index c377d2fdb4413..1e6594b551fb2 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp @@ -21,6 +21,7 @@ #include "DWARFDIE.h" #include "Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h" +#include "swift/Demangling/ManglingFlavor.h" #include "swift/RemoteInspection/TypeLowering.h" #include "lldb/Utility/LLDBLog.h" @@ -63,7 +64,9 @@ getTypeAndDie(TypeSystemSwiftTypeRef &ts, const swift::reflection::TypeRef *TR) { swift::Demangle::Demangler dem; swift::Demangle::NodePointer node = TR->getDemangling(dem); - auto type = ts.RemangleAsType(dem, node); + // TODO: mangling flavor should come from the TypeRef. + auto type = + ts.RemangleAsType(dem, node, swift::Mangle::ManglingFlavor::Embedded); if (!type) { if (auto log = GetLog(LLDBLog::Types)) { std::stringstream ss; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h index cb3ae8a06d788..883789a223bb4 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -76,6 +76,8 @@ class DWARFIndex { StatsDuration::Duration GetIndexTime() { return m_index_time; } + void ResetStatistics() { m_index_time.reset(); } + protected: Module &m_module; StatsDuration m_index_time; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 6b70500220974..5bffd66b955ca 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -4552,6 +4552,12 @@ StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() { return {}; } +void SymbolFileDWARF::ResetStatistics() { + m_parse_time.reset(); + if (m_index) + return m_index->ResetStatistics(); +} + Status SymbolFileDWARF::CalculateFrameVariableError(StackFrame &frame) { std::lock_guard guard(GetModuleMutex()); CompileUnit *cu = frame.GetSymbolContext(eSymbolContextCompUnit).comp_unit; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 31a69154f2181..44354594e847d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -336,6 +336,8 @@ class SymbolFileDWARF : public SymbolFileCommon { StatsDuration &GetDebugInfoParseTimeRef() { return m_parse_time; } + void ResetStatistics() override; + virtual lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data, const lldb::offset_t data_offset, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 6675e10a36d70..a80d53a32a949 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -3862,6 +3862,34 @@ bool TypeSystemClang::SupportsLanguage(lldb::LanguageType language) { return TypeSystemClangSupportsLanguage(language); } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool TypeSystemClang::IsBoundsSafetyIndexable(lldb::opaque_compiler_type_t type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + + if (qual_type->isPointerType()) { + const PointerType *pointer_type = qual_type->getAs(); + + if (pointer_type) + return pointer_type->getPointerAttributes().isIndexable(); + } + + return false; +} + +bool TypeSystemClang::IsBoundsSafetyBidiIndexable(lldb::opaque_compiler_type_t type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + + if (qual_type->isPointerType()) { + const PointerType *pointer_type = qual_type->getAs(); + + if (pointer_type) + return pointer_type->getPointerAttributes().isBidiIndexable(); + } + + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + std::optional TypeSystemClang::GetCXXClassName(const CompilerType &type) { if (!type) @@ -4801,6 +4829,56 @@ TypeSystemClang::AddRestrictModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +CompilerType +TypeSystemClang::AddBoundsSafetyIndexableAttribute(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType result(GetQualType(type)); + + if (result->isPointerType()) + if (const PointerType *PT = result->getAs()) { + BoundsSafetyPointerAttributes Attr = PT->getPointerAttributes(); + Attr.setIndexable(); + QualType PointeeTy = result->getPointeeType(); + return GetType(getASTContext().getPointerType(PointeeTy, Attr)); + } + } + return CompilerType(); +} + +CompilerType +TypeSystemClang::AddBoundsSafetyBidiIndexableAttribute(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType result(GetQualType(type)); + + if (result->isPointerType()) + if (const PointerType *PT = result->getAs()) { + BoundsSafetyPointerAttributes Attr = PT->getPointerAttributes(); + Attr.setBidiIndexable(); + QualType PointeeTy = result->getPointeeType(); + return GetType(getASTContext().getPointerType(PointeeTy, Attr)); + } + } + return CompilerType(); +} + +CompilerType +TypeSystemClang::AddBoundsSafetyUnspecifiedAttribute(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType result(GetQualType(type)); + + if (result->isPointerType()) + if (const PointerType *PT = result->getAs()) { + BoundsSafetyPointerAttributes Attr = PT->getPointerAttributes(); + Attr.setUnspecified(); + QualType PointeeTy = result->getPointeeType(); + return GetType(getASTContext().getPointerType(PointeeTy, Attr)); + } + } + return CompilerType(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + CompilerType TypeSystemClang::CreateTypedef( lldb::opaque_compiler_type_t type, const char *typedef_name, const CompilerDeclContext &compiler_decl_ctx, uint32_t payload) { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 21cb95c773adb..162f4c3d03086 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -792,6 +792,11 @@ class TypeSystemClang : public TypeSystem { bool SupportsLanguage(lldb::LanguageType language) override; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool IsBoundsSafetyIndexable(lldb::opaque_compiler_type_t type) override; + bool IsBoundsSafetyBidiIndexable(lldb::opaque_compiler_type_t type) override; + /* TO_UPSTREAM(BoundsSafety) OFF */ + static std::optional GetCXXClassName(const CompilerType &type); // Type Completion @@ -873,6 +878,12 @@ class TypeSystemClang : public TypeSystem { CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type) override; + /* TO_UPSTREAM(BoundsSafety) ON */ + CompilerType AddBoundsSafetyIndexableAttribute(lldb::opaque_compiler_type_t type) override; + CompilerType AddBoundsSafetyBidiIndexableAttribute(lldb::opaque_compiler_type_t type) override; + CompilerType AddBoundsSafetyUnspecifiedAttribute(lldb::opaque_compiler_type_t type) override; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Using the current type, create a new typedef to that type using /// "typedef_name" as the name and "decl_ctx" as the decl context. /// \param opaque_payload is an opaque TypePayloadClang. diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp index 809b6a3ccd7f1..839c9539c0986 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp @@ -46,6 +46,7 @@ #include "swift/Basic/PrimarySpecificPaths.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Demangling/Demangle.h" +#include "swift/Demangling/ManglingFlavor.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/ModuleInterfaceLoader.h" @@ -1311,9 +1312,10 @@ static bool DeserializeAllCompilerFlags(swift::CompilerInvocation &invocation, known_##NAME.insert(path KEY); \ } - INIT_SEARCH_PATH_SET(std::string, getImportSearchPaths(), - import_search_paths, ); - INIT_SEARCH_PATH_SET(swift::SearchPathOptions::FrameworkSearchPath, + INIT_SEARCH_PATH_SET(swift::SearchPathOptions::SearchPath, + getImportSearchPaths(), import_search_paths, + .Path); + INIT_SEARCH_PATH_SET(swift::SearchPathOptions::SearchPath, getFrameworkSearchPaths(), framework_search_paths, .Path); @@ -1397,10 +1399,12 @@ static bool DeserializeAllCompilerFlags(swift::CompilerInvocation &invocation, for (auto &searchPath : searchPaths) { std::string path = remap(searchPath.Path); if (!searchPath.IsFramework) { + swift::SearchPathOptions::SearchPath + import_search_path(path, searchPath.IsSystem); if (known_import_search_paths.insert(path).second) - import_search_paths.push_back(path); + import_search_paths.push_back(import_search_path); } else { - swift::SearchPathOptions::FrameworkSearchPath + swift::SearchPathOptions::SearchPath framework_search_path(path, searchPath.IsSystem); if (known_framework_search_paths.insert(path).second) framework_search_paths.push_back(framework_search_path); @@ -1800,7 +1804,7 @@ static void applyOverrideOptions(std::vector &args, void SwiftASTContext::AddExtraClangArgs( const std::vector &ExtraArgs, - const std::vector &module_search_paths, + const std::vector> module_search_paths, const std::vector> framework_search_paths, StringRef overrideOpts) { swift::ClangImporterOptions &importer_options = GetClangImporterOptions(); @@ -1849,7 +1853,7 @@ void SwiftASTContext::AddExtraClangArgs( void SwiftASTContext::AddExtraClangCC1Args( const std::vector &source, - const std::vector &module_search_paths, + const std::vector> module_search_paths, const std::vector> framework_search_paths, std::vector &dest) { clang::CompilerInvocation invocation; @@ -1870,7 +1874,7 @@ void SwiftASTContext::AddExtraClangCC1Args( // additional clang modules when doing type reconstruction. for (auto &path : module_search_paths) { clangArgs.push_back("-I"); - clangArgs.push_back(path.c_str()); + clangArgs.push_back(path.first.c_str()); } for (auto &path : default_paths) { llvm::SmallString<128> search_path(GetPlatformSDKPath()); @@ -2203,7 +2207,7 @@ ProcessModule(Module &module, std::string m_description, bool is_main_executable, StringRef module_filter, llvm::Triple triple, std::vector &plugin_search_options, - std::vector &module_search_paths, + std::vector> &module_search_paths, std::vector> &framework_search_paths, std::vector &extra_clang_args, std::string &error) { @@ -2300,7 +2304,7 @@ ProcessModule(Module &module, std::string m_description, bool exists = false; llvm::sys::fs::is_directory(path, exists); if (exists) - module_search_paths.push_back(std::string(path)); + module_search_paths.push_back({std::string(path), /*system*/ false}); } // Create a one-off CompilerInvocation as a place to load the @@ -2333,9 +2337,8 @@ ProcessModule(Module &module, std::string m_description, plugin_search_options.insert(plugin_search_options.end(), opts.PluginSearchOpts.begin(), opts.PluginSearchOpts.end()); - module_search_paths.insert(module_search_paths.end(), - opts.getImportSearchPaths().begin(), - opts.getImportSearchPaths().end()); + for (auto path : opts.getImportSearchPaths()) + module_search_paths.push_back({path.Path, path.IsSystem}); for (auto path : opts.getFrameworkSearchPaths()) framework_search_paths.push_back({path.Path, path.IsSystem}); auto &clang_opts = invocation.getClangImporterOptions().ExtraArgs; @@ -2372,7 +2375,7 @@ SwiftASTContext::CreateInstance(lldb::LanguageType language, Module &module, ss << '"' << ')'; } LLDB_SCOPED_TIMERF("%s::CreateInstance", m_description.c_str()); - std::vector module_search_paths; + std::vector> module_search_paths; std::vector> framework_search_paths; LOG_PRINTF(GetLog(LLDBLog::Types), "(Module)"); @@ -3024,7 +3027,7 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc, ConfigureModuleCachePath(*swift_ast_sp); std::vector plugin_search_options; - std::vector module_search_paths; + std::vector> module_search_paths; std::vector> framework_search_paths; std::vector extra_clang_args; @@ -3036,10 +3039,11 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc, use_all_compiler_flags = !got_serialized_options || target_sp->GetUseAllCompilerFlags(); + const bool is_system = false; + for (const FileSpec &path : target_sp->GetSwiftModuleSearchPaths()) - module_search_paths.push_back(path.GetPath()); + module_search_paths.push_back({path.GetPath(), is_system}); - const bool is_system = false; for (const FileSpec &path : target_sp->GetSwiftFrameworkSearchPaths()) framework_search_paths.push_back({path.GetPath(), is_system}); } @@ -3527,7 +3531,7 @@ swift::SearchPathOptions &SwiftASTContext::GetSearchPathOptions() { } void SwiftASTContext::InitializeSearchPathOptions( - llvm::ArrayRef extra_module_search_paths, + llvm::ArrayRef> extra_module_search_paths, llvm::ArrayRef> extra_framework_search_paths) { LLDB_SCOPED_TIMER(); swift::CompilerInvocation &invocation = GetCompilerInvocation(); @@ -3606,24 +3610,24 @@ void SwiftASTContext::InitializeSearchPathOptions( } llvm::StringMap processed; - std::vector invocation_import_paths( + std::vector invocation_import_paths( invocation.getSearchPathOptions().getImportSearchPaths()); // Add all deserialized paths to the map. for (const auto &path : invocation_import_paths) - processed.insert({path, false}); + processed.insert({path.Path, path.IsSystem}); // Add/unique all extra paths. for (const auto &path : extra_module_search_paths) { - auto it_notseen = processed.insert({path, false}); + auto it_notseen = processed.insert(path); if (it_notseen.second) - invocation_import_paths.push_back(path); + invocation_import_paths.push_back({path.first, path.second}); } invocation.getSearchPathOptions().setImportSearchPaths( invocation_import_paths); // This preserves the IsSystem bit, but deduplicates entries ignoring it. processed.clear(); - std::vector + std::vector invocation_framework_paths( invocation.getSearchPathOptions().getFrameworkSearchPaths()); // Add all deserialized paths to the map. @@ -3873,8 +3877,14 @@ swift::ModuleDecl *SwiftASTContext::GetCachedModule(std::string module_name) { } llvm::Expected -SwiftASTContext::CreateModule(std::string module_name, - swift::ImplicitImportInfo importInfo) { +SwiftASTContext::CreateEmptyModule(std::string module_name) { + return CreateModule(module_name, /*importInfo*/ {}, + /*populateFiles*/ [](auto, auto) {}); +} + +llvm::Expected SwiftASTContext::CreateModule( + std::string module_name, swift::ImplicitImportInfo importInfo, + swift::ModuleDecl::PopulateFilesFn populateFiles) { VALID_OR_RETURN(llvm::createStringError("no context")); if (module_name.empty()) return llvm::createStringError("invalid module name (empty)"); @@ -3888,7 +3898,8 @@ SwiftASTContext::CreateModule(std::string module_name, return llvm::createStringError("invalid swift AST (nullptr)"); swift::Identifier module_id(ast->getIdentifier(module_name)); - auto *module_decl = swift::ModuleDecl::create(module_id, **ast, importInfo); + auto *module_decl = + swift::ModuleDecl::create(module_id, **ast, importInfo, populateFiles); if (!module_decl) return llvm::createStringError( llvm::formatv("failed to create module for \"{0}\"", module_name)); @@ -4041,7 +4052,7 @@ SwiftASTContext::GetModule(const FileSpec &module_spec) { std::string module_directory(module_spec.GetDirectory().GetCString()); bool add_search_path = true; for (auto path : ast->SearchPathOpts.getImportSearchPaths()) { - if (path == module_directory) { + if (path.Path == module_directory) { add_search_path = false; break; } @@ -4602,7 +4613,11 @@ ConstString SwiftASTContext::GetMangledTypeName(swift::TypeBase *type_base) { assert(!swift_type->hasArchetype() && "type has not been mapped out of context"); - swift::Mangle::ASTMangler mangler(true); + ThreadSafeASTContext ast_ctx = GetASTContext(); + if (!ast_ctx) + return {}; + + swift::Mangle::ASTMangler mangler(**ast_ctx, true); std::string s = mangler.mangleTypeForDebugger(swift_type, nullptr); if (s.empty()) return {}; @@ -4848,7 +4863,7 @@ static CompilerType ValueDeclToType(swift::ValueDecl *decl) { swift::TypeAliasDecl *alias_decl = swift::cast(decl); swift::Type swift_type = swift::TypeAliasType::get( - alias_decl, swift::Type(), swift::SubstitutionMap(), + alias_decl, swift::Type(), llvm::ArrayRef(), alias_decl->getUnderlyingType()); return ToCompilerType({swift_type.getPointer()}); } @@ -4907,7 +4922,7 @@ static SwiftASTContext::TypeOrDecl DeclToTypeOrDecl(swift::Decl *decl) { swift::TypeAliasDecl *alias_decl = swift::cast(decl); swift::Type swift_type = swift::TypeAliasType::get( - alias_decl, swift::Type(), swift::SubstitutionMap(), + alias_decl, swift::Type(), llvm::ArrayRef(), alias_decl->getUnderlyingType()); return ToCompilerType(swift_type.getPointer()); } @@ -5185,9 +5200,8 @@ swift::ModuleDecl *SwiftASTContext::GetScratchModule() { if (m_scratch_module == nullptr) { ThreadSafeASTContext ast_ctx = GetASTContext(); - m_scratch_module = swift::ModuleDecl::create( - GetASTContext()->getIdentifier("__lldb_scratch_module"), - **ast_ctx); + m_scratch_module = swift::ModuleDecl::createEmpty( + GetASTContext()->getIdentifier("__lldb_scratch_module"), **ast_ctx); } return m_scratch_module; } @@ -5328,10 +5342,12 @@ SwiftASTContext::GetNonTriviallyManagedReferenceKind( return {}; } -CompilerType -SwiftASTContext::CreateGenericTypeParamType(unsigned int depth, - unsigned int index) { +CompilerType SwiftASTContext::CreateGenericTypeParamType( + unsigned int depth, unsigned int index, + swift::Mangle::ManglingFlavor flavor) { ThreadSafeASTContext ast_ctx = GetASTContext(); + auto ast_flavor = GetManglingFlavor(); + assert(flavor == ast_flavor && "Requested flavor and ast flavor diverge!"); return ToCompilerType( swift::GenericTypeParamType::getType(depth, index, **ast_ctx)); } @@ -5449,7 +5465,7 @@ void SwiftASTContextForExpressions::ModulesDidLoad(ModuleList &module_list) { unsigned num_images = module_list.GetSize(); for (size_t mi = 0; mi != num_images; ++mi) { std::vector plugin_search_options; - std::vector module_search_paths; + std::vector> module_search_paths; std::vector> framework_search_paths; std::vector extra_clang_args; lldb::ModuleSP module_sp = module_list.GetModuleAtIndex(mi); @@ -5543,9 +5559,9 @@ void SwiftASTContext::LogConfiguration(bool is_repl) { (unsigned long long)m_ast_context_ap->SearchPathOpts .getImportSearchPaths() .size()); - for (const std::string &import_search_path : + for (const auto &import_search_path : m_ast_context_ap->SearchPathOpts.getImportSearchPaths()) - HEALTH_LOG_PRINTF(" %s", import_search_path.c_str()); + HEALTH_LOG_PRINTF(" %s", import_search_path.Path.c_str()); swift::ClangImporterOptions &clang_importer_options = GetClangImporterOptions(); @@ -5999,7 +6015,8 @@ ConstString SwiftASTContext::GetTypeName(opaque_compiler_type_t type, /// Build a dictionary of Archetype names that appear in \p type. static llvm::DenseMap GetArchetypeNames(swift::Type swift_type, swift::ASTContext &ast_ctx, - const SymbolContext *sc) { + const SymbolContext *sc, + swift::Mangle::ManglingFlavor flavor) { LLDB_SCOPED_TIMER(); llvm::DenseMap dict; @@ -6009,7 +6026,7 @@ GetArchetypeNames(swift::Type swift_type, swift::ASTContext &ast_ctx, llvm::DenseMap, StringRef> names; SwiftLanguageRuntime::GetGenericParameterNamesForFunction(*sc, nullptr, - names); + flavor, names); swift_type.visit([&](swift::Type type) { if (!type->isTypeParameter() || dict.count(type->getCanonicalType())) return; @@ -6038,7 +6055,8 @@ ConstString SwiftASTContext::GetDisplayTypeName(opaque_compiler_type_t type, print_options.SynthesizeSugarOnTypes = true; print_options.FullyQualifiedTypesIfAmbiguous = true; ThreadSafeASTContext ast_ctx = GetASTContext(); - auto dict = GetArchetypeNames(swift_type, **ast_ctx, sc); + auto dict = + GetArchetypeNames(swift_type, **ast_ctx, sc, GetManglingFlavor()); print_options.AlternativeTypeNames = &dict; type_name = swift_type.getString(print_options); } @@ -9304,3 +9322,9 @@ bool SwiftASTContext::GetCompileUnitImportsImpl( } return true; } + +swift::Mangle::ManglingFlavor SwiftASTContext::GetManglingFlavor() { + return GetASTContext()->LangOpts.hasFeature(swift::Feature::Embedded) + ? swift::Mangle::ManglingFlavor::Embedded + : swift::Mangle::ManglingFlavor::Default; +} diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h index c2d27181153c4..1af8e1c611fd0 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h @@ -24,6 +24,7 @@ #include "swift/AST/Import.h" #include "swift/AST/Module.h" +#include "swift/Demangling/ManglingFlavor.h" #include "swift/Parse/ParseVersion.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" @@ -252,7 +253,7 @@ class SwiftASTContext : public TypeSystemSwift { swift::SerializationOptions &GetSerializationOptions(); void InitializeSearchPathOptions( - llvm::ArrayRef module_search_paths, + llvm::ArrayRef> module_search_paths, llvm::ArrayRef> framework_search_paths); swift::ClangImporterOptions &GetClangImporterOptions(); @@ -276,13 +277,13 @@ class SwiftASTContext : public TypeSystemSwift { /// apply the working directory to any relative paths. void AddExtraClangArgs( const std::vector &ExtraArgs, - const std::vector &module_search_paths, + const std::vector> module_search_paths, const std::vector> framework_search_paths, llvm::StringRef overrideOpts = ""); void AddExtraClangCC1Args( const std::vector &source, - const std::vector &module_search_paths, + const std::vector> module_search_paths, const std::vector> framework_search_paths, std::vector &dest); static void AddExtraClangArgs(const std::vector& source, @@ -310,12 +311,19 @@ class SwiftASTContext : public TypeSystemSwift { /// \return the ExtraArgs of the ClangImporterOptions. const std::vector &GetClangArguments(); + /// Attempt to create an empty Swift module. + llvm::Expected + CreateEmptyModule(std::string module_name); + /// Attempt to create a Swift module. /// /// \param importInfo Information about which modules should be implicitly /// imported by each file of the module. + /// \param populateFiles A function which populates the files for the module. + /// Once called, the module's list of files may not change. llvm::Expected - CreateModule(std::string module_name, swift::ImplicitImportInfo importInfo); + CreateModule(std::string module_name, swift::ImplicitImportInfo importInfo, + swift::ModuleDecl::PopulateFilesFn populateFiles); // This function should only be called when all search paths // for all items in a swift::ASTContext have been setup to @@ -460,8 +468,9 @@ class SwiftASTContext : public TypeSystemSwift { lldb::opaque_compiler_type_t type) override; /// Creates a GenericTypeParamType with the desired depth and index. - CompilerType CreateGenericTypeParamType(unsigned int depth, - unsigned int index) override; + CompilerType + CreateGenericTypeParamType(unsigned int depth, unsigned int index, + swift::Mangle::ManglingFlavor flavor) override; CompilerType GetErrorType() override; @@ -853,6 +862,9 @@ class SwiftASTContext : public TypeSystemSwift { void PerformCompileUnitImports(const SymbolContext &sc, lldb::ProcessSP process_sp, Status &error); + /// Returns the mangling flavor associated with this ASTContext. + swift::Mangle::ManglingFlavor GetManglingFlavor(); + protected: bool GetCompileUnitImportsImpl( const SymbolContext &sc, lldb::ProcessSP process_sp, diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h b/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h index 4682afa45853e..dbef04426c9ff 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h @@ -15,6 +15,7 @@ #include "swift/Demangling/Demangle.h" #include "swift/Demangling/Demangler.h" +#include "swift/Demangling/ManglingFlavor.h" #include "llvm/ADT/ArrayRef.h" namespace lldb_private { @@ -122,23 +123,24 @@ CreateNominal(swift::Demangle::Demangler &dem, swift::Demangle::Node::Kind kind, } /// Produce a type mangling for a class. -inline ManglingErrorOr MangleClass(swift::Demangle::Demangler &dem, - llvm::StringRef module_name, - llvm::StringRef class_name) { +inline ManglingErrorOr +MangleClass(swift::Demangle::Demangler &dem, llvm::StringRef module_name, + llvm::StringRef class_name, swift::Mangle::ManglingFlavor flavor) { auto *node = CreateNominal(dem, Node::Kind::Class, module_name, class_name); - return mangleNode(MangleType(dem, node)); + return mangleNode(MangleType(dem, node), flavor); } /// Create a mangled name for a type node. inline swift::Demangle::ManglingErrorOr GetMangledName(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node) { + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor) { using namespace swift::Demangle; auto global = dem.createNode(Node::Kind::Global); auto type_mangling = dem.createNode(Node::Kind::TypeMangling); global->addChild(type_mangling, dem); type_mangling->addChild(node, dem); - return mangleNode(global); + return mangleNode(global, flavor); } } // namespace swift_demangle diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h index 82cf7bf16574a..91315aa458d85 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h @@ -21,6 +21,7 @@ #include "lldb/Utility/Flags.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-private.h" +#include "swift/Demangling/ManglingFlavor.h" namespace clang { class Decl; @@ -169,9 +170,10 @@ class TypeSystemSwift : public TypeSystem { GetNonTriviallyManagedReferenceKind(lldb::opaque_compiler_type_t type) = 0; /// Creates a GenericTypeParamType with the desired depth and index. - virtual CompilerType CreateGenericTypeParamType(unsigned int depth, - unsigned int index) = 0; - + virtual CompilerType + CreateGenericTypeParamType(unsigned int depth, unsigned int index, + swift::Mangle::ManglingFlavor flavor) = 0; + using TypeSystem::DumpTypeDescription; virtual void DumpTypeDescription( lldb::opaque_compiler_type_t type, bool print_help_if_available, @@ -250,6 +252,14 @@ class TypeSystemSwift : public TypeSystem { CompilerType *function_pointer_type_ptr) override { return false; } + /* TO_UPSTREAM(BoundsSafety) ON */ + bool IsBoundsSafetyIndexable(lldb::opaque_compiler_type_t type) override { + return false; + } + bool IsBoundsSafetyBidiIndexable(lldb::opaque_compiler_type_t type) override { + return false; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ bool IsMemberFunctionPointerType( lldb::opaque_compiler_type_t type) override { return false; diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp index c4178f06c0c49..3455aeaca2e19 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp @@ -26,18 +26,23 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/DumpDataExtractor.h" #include "lldb/Host/StreamFile.h" +#include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" +#include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Timer.h" -#include "swift/ClangImporter/ClangImporter.h" +#include "lldb/lldb-enumerations.h" #include "swift/../../lib/ClangImporter/ClangAdapter.h" +#include "swift/ClangImporter/ClangImporter.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/Demangler.h" +#include "swift/Demangling/ManglingFlavor.h" #include "swift/Frontend/Frontend.h" #include "clang/APINotes/APINotesManager.h" @@ -180,6 +185,7 @@ std::string TypeSystemSwiftTypeRef::AdjustTypeForOriginallyDefinedInModule( if (mangled_typename.empty()) return {}; + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_typename); swift::Demangle::Demangler dem; auto *type_node = swift_demangle::GetDemangledTypeMangling(dem, mangled_typename); @@ -243,7 +249,7 @@ std::string TypeSystemSwiftTypeRef::AdjustTypeForOriginallyDefinedInModule( if (node->getKind() != Node::Kind::Type) return true; - auto compiler_type = RemangleAsType(dem, node); + auto compiler_type = RemangleAsType(dem, node, flavor); if (!compiler_type) return true; @@ -488,6 +494,8 @@ NodePointer TypeSystemSwiftTypeRef::CreateBoundGenericStruct( CompilerType TypeSystemSwiftTypeRef::CreateClangStructType(llvm::StringRef name) { + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(name); + using namespace swift::Demangle; Demangler dem; NodePointer module = dem.createNodeWithAllocatedText( @@ -499,7 +507,7 @@ TypeSystemSwiftTypeRef::CreateClangStructType(llvm::StringRef name) { nominal->addChild(identifier, dem); NodePointer type = dem.createNode(Node::Kind::Type); type->addChild(nominal, dem); - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); } /// Return a demangle tree leaf node representing \p clang_type. @@ -946,6 +954,7 @@ IsClangImportedType(NodePointer node, std::pair TypeSystemSwiftTypeRef::ResolveTypeAlias(swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool prefer_clang_types) { LLDB_SCOPED_TIMER(); @@ -958,7 +967,7 @@ TypeSystemSwiftTypeRef::ResolveTypeAlias(swift::Demangle::Demangler &dem, // Try to look this up as a Swift type alias. For each *Swift* // type alias there is a debug info entry that has the mangled // name as name and the aliased type as a type. - auto mangling = GetMangledName(dem, node); + auto mangling = GetMangledName(dem, node, flavor); if (!mangling.isSuccess()) { LLDB_LOGF(GetLog(LLDBLog::Types), "Failed while mangling type alias (%d:%u)", mangling.error().code, @@ -1064,7 +1073,9 @@ TypeSystemSwiftTypeRef::GetTupleElement(lldb::opaque_compiler_type_t type, TupleElement result; using namespace swift::Demangle; Demangler dem; - NodePointer node = TypeSystemSwiftTypeRef::DemangleCanonicalOutermostType(dem, type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(AsMangledName(type)); + NodePointer node = + TypeSystemSwiftTypeRef::DemangleCanonicalOutermostType(dem, type); if (!node || node->getKind() != Node::Kind::Tuple) return {}; if (node->getNumChildren() < idx) @@ -1076,7 +1087,7 @@ TypeSystemSwiftTypeRef::GetTupleElement(lldb::opaque_compiler_type_t type, for (NodePointer n : *child) { switch (n->getKind()) { case Node::Kind::Type: - result.element_type = RemangleAsType(dem, n); + result.element_type = RemangleAsType(dem, n, flavor); break; case Node::Kind::TupleElementName: result.element_name = ConstString(n->getText()); @@ -1172,7 +1183,8 @@ Desugar(swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, swift::Demangle::NodePointer TypeSystemSwiftTypeRef::Canonicalize(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node) { + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor) { assert(node); auto kind = node->getKind(); switch (kind) { @@ -1205,7 +1217,7 @@ TypeSystemSwiftTypeRef::Canonicalize(swift::Demangle::Demangler &dem, case Node::Kind::BoundGenericTypeAlias: case Node::Kind::TypeAlias: { - auto node_clangtype = ResolveTypeAlias(dem, node); + auto node_clangtype = ResolveTypeAlias(dem, node, flavor); if (CompilerType clang_type = node_clangtype.second) { if (auto result = GetClangTypeNode(clang_type, dem)) return result; @@ -1226,10 +1238,12 @@ TypeSystemSwiftTypeRef::Canonicalize(swift::Demangle::Demangler &dem, /// desugared types in the debug info of module \p M. swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetCanonicalNode(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node) { + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor) { using namespace swift::Demangle; - return TypeSystemSwiftTypeRef::Transform( - dem, node, [&](NodePointer node) { return Canonicalize(dem, node); }); + return TypeSystemSwiftTypeRef::Transform(dem, node, [&](NodePointer node) { + return Canonicalize(dem, node, flavor); + }); } /// Return the demangle tree representation of this type's canonical @@ -1237,7 +1251,9 @@ TypeSystemSwiftTypeRef::GetCanonicalNode(swift::Demangle::Demangler &dem, swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetCanonicalDemangleTree( swift::Demangle::Demangler &dem, StringRef mangled_name) { auto *node = dem.demangleSymbol(mangled_name); - return GetCanonicalNode(dem, node); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + + return GetCanonicalNode(dem, node, flavor); } static clang::Decl *GetDeclForTypeAndKind(clang::QualType qual_type, @@ -1371,13 +1387,12 @@ static bool IsImportedType(swift::Demangle::NodePointer node) { return false; } -swift::Demangle::NodePointer -TypeSystemSwiftTypeRef::GetSwiftified(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node, - bool resolve_objc_module) { +swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetSwiftified( + swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool resolve_objc_module) { LLDB_SCOPED_TIMER(); - auto mangling = GetMangledName(dem, node); + auto mangling = GetMangledName(dem, node, flavor); if (!mangling.isSuccess()) { LLDB_LOGF(GetLog(LLDBLog::Types), "Failed while getting swiftified (%d:%u)", mangling.error().code, mangling.error().line); @@ -1442,7 +1457,7 @@ TypeSystemSwiftTypeRef::GetSwiftified(swift::Demangle::Demangler &dem, swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetNodeForPrintingImpl( swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, - bool resolve_objc_module) { + swift::Mangle::ManglingFlavor flavor, bool resolve_objc_module) { using namespace swift::Demangle; return Transform(dem, node, [&](NodePointer node) { NodePointer canonical = node; @@ -1451,7 +1466,7 @@ swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetNodeForPrintingImpl( case Node::Kind::Class: case Node::Kind::Structure: case Node::Kind::TypeAlias: - return GetSwiftified(dem, node, resolve_objc_module); + return GetSwiftified(dem, node, flavor, resolve_objc_module); // // The remaining cases are all about bug-for-bug compatibility @@ -1549,8 +1564,10 @@ swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetDemangleTreeForPrinting( bool resolve_objc_module) { LLDB_SCOPED_TIMER(); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + auto *node = dem.demangleSymbol(mangled_name); - return GetNodeForPrintingImpl(dem, node, resolve_objc_module); + return GetNodeForPrintingImpl(dem, node, flavor, resolve_objc_module); } /// Determine wether this demangle tree contains a node of kind \c kind. @@ -1579,10 +1596,9 @@ static bool ContainsGenericTypeParameter(swift::Demangle::NodePointer node) { /// determine whether a node is generic or not, it needs to visit all /// nodes. The \p generic_walk argument specifies that the primary /// attributes have been collected and that we only look for generics. -uint32_t -TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node, - bool &unresolved_typealias) { +uint32_t TypeSystemSwiftTypeRef::CollectTypeInfo( + swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool &unresolved_typealias) { if (!node) return 0; uint32_t swift_flags = eTypeIsSwift; @@ -1612,7 +1628,7 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, if ((type_class & eTypeClassBuiltin)) { swift_flags &= ~eTypeIsStructUnion; swift_flags |= CollectTypeInfo(dem, GetClangTypeNode(clang_type, dem), - unresolved_typealias); + flavor, unresolved_typealias); return; } }; @@ -1714,7 +1730,7 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, // Variadic generic types. auto typelist = node->getChild(1); bool ignore; - auto param_flags = CollectTypeInfo(dem, typelist, ignore); + auto param_flags = CollectTypeInfo(dem, typelist, flavor, ignore); swift_flags |= param_flags & eTypeIsPack; } @@ -1723,7 +1739,7 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, if (!IsClangImportedType(node, decl_context)) break; - auto mangling = GetMangledName(dem, node); + auto mangling = GetMangledName(dem, node, flavor); if (!mangling.isSuccess()) { LLDB_LOGF(GetLog(LLDBLog::Types), "Failed mangling while collecting type infos (%d:%u)", @@ -1773,7 +1789,7 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, case Node::Kind::TypeAlias: { // Bug-for-bug compatibility. // swift_flags |= eTypeIsTypedef; - auto node_clangtype = ResolveTypeAlias(dem, node); + auto node_clangtype = ResolveTypeAlias(dem, node, flavor); if (CompilerType clang_type = node_clangtype.second) { collect_clang_type(clang_type); return swift_flags; @@ -1783,8 +1799,8 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, // then we don't have debug info to resolve it from. unresolved_typealias = true; } - swift_flags |= - CollectTypeInfo(dem, node_clangtype.first, unresolved_typealias); + swift_flags |= CollectTypeInfo(dem, node_clangtype.first, flavor, + unresolved_typealias); return swift_flags; } case Node::Kind::PackExpansion: @@ -1810,7 +1826,7 @@ TypeSystemSwiftTypeRef::CollectTypeInfo(swift::Demangle::Demangler &dem, // Visit the child nodes. for (auto *child : *node) - swift_flags |= CollectTypeInfo(dem, child, unresolved_typealias); + swift_flags |= CollectTypeInfo(dem, child, flavor, unresolved_typealias); return swift_flags; } @@ -2295,7 +2311,9 @@ bool TypeSystemSwiftTypeRef::Verify(opaque_compiler_type_t type) { using namespace swift::Demangle; Demangler dem; NodePointer node = dem.demangleSymbol(str); - auto mangling = mangleNode(node); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(str); + + auto mangling = mangleNode(node, flavor); if (!mangling.isSuccess()) return false; std::string remangled = mangling.result(); @@ -2444,10 +2462,11 @@ template <> bool Equivalent(CompilerType l, CompilerType r) { ContainsGenericTypeParameter(r_node) || ContainsSugaredParen(r_node)) return true; auto l_mangling = swift::Demangle::mangleNode( - TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, l_node)); + TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, l_node), + SwiftLanguageRuntime::GetManglingFlavor(l.GetMangledTypeName())); auto r_mangling = swift::Demangle::mangleNode( - TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, r_node)); - + TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, r_node), + SwiftLanguageRuntime::GetManglingFlavor(r.GetMangledTypeName())); if (!l_mangling.isSuccess() || !r_mangling.isSuccess()) { llvm::dbgs() << "TypeSystemSwiftTypeRef diverges from SwiftASTContext " "(mangle error): " @@ -2681,7 +2700,8 @@ constexpr ExecutionContextScope *g_no_exe_ctx = nullptr; CompilerType TypeSystemSwiftTypeRef::RemangleAsType(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node) { + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor) { if (!node) return {}; @@ -2697,7 +2717,7 @@ TypeSystemSwiftTypeRef::RemangleAsType(swift::Demangle::Demangler &dem, global->addChild(node, dem); node = global; } - auto mangling = mangleNode(node); + auto mangling = mangleNode(node, flavor); if (!mangling.isSuccess()) return {}; ConstString mangled_element(mangling.result()); @@ -2705,7 +2725,7 @@ TypeSystemSwiftTypeRef::RemangleAsType(swift::Demangle::Demangler &dem, } swift::Demangle::NodePointer TypeSystemSwiftTypeRef::DemangleCanonicalType( - swift::Demangle::Demangler &dem, opaque_compiler_type_t opaque_type) { + swift::Demangle::Demangler &dem, lldb::opaque_compiler_type_t opaque_type) { using namespace swift::Demangle; CompilerType type = GetCanonicalType(opaque_type); return GetDemangledType(dem, type.GetMangledTypeName().GetStringRef()); @@ -2715,10 +2735,13 @@ swift::Demangle::NodePointer TypeSystemSwiftTypeRef::DemangleCanonicalOutermostType( swift::Demangle::Demangler &dem, lldb::opaque_compiler_type_t type) { using namespace swift::Demangle; - NodePointer node = GetDemangledType(dem, AsMangledName(type)); + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + NodePointer node = GetDemangledType(dem, mangled_name); + if (!node) return nullptr; - NodePointer canonical = Canonicalize(dem, node); + NodePointer canonical = Canonicalize(dem, node, flavor); if (canonical && canonical->getKind() == swift::Demangle::Node::Kind::TypeAlias) { // If this is a typealias defined in the expression evaluator, @@ -2730,9 +2753,9 @@ TypeSystemSwiftTypeRef::DemangleCanonicalOutermostType( return canonical; } -CompilerType -TypeSystemSwiftTypeRef::CreateGenericTypeParamType(unsigned int depth, - unsigned int index) { +CompilerType TypeSystemSwiftTypeRef::CreateGenericTypeParamType( + unsigned int depth, unsigned int index, + swift::Mangle::ManglingFlavor flavor) { Demangler dem; NodePointer type_node = dem.createNode(Node::Kind::Type); @@ -2745,7 +2768,7 @@ TypeSystemSwiftTypeRef::CreateGenericTypeParamType(unsigned int depth, dep_type_node->addChild(depth_node, dem); dep_type_node->addChild(index_node, dem); - auto type = RemangleAsType(dem, type_node); + auto type = RemangleAsType(dem, type_node, flavor); return type; } @@ -2754,6 +2777,10 @@ bool TypeSystemSwiftTypeRef::IsArrayType(opaque_compiler_type_t type, uint64_t *size, bool *is_incomplete) { auto impl = [&]() { using namespace swift::Demangle; + + auto mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalOutermostType(dem, type); if (!node || node->getNumChildren() != 2 || @@ -2783,7 +2810,7 @@ bool TypeSystemSwiftTypeRef::IsArrayType(opaque_compiler_type_t type, if (element_type) // FIXME: This expensive canonicalization is only there for // SwiftASTContext compatibility. - *element_type = RemangleAsType(dem, elem_node).GetCanonicalType(); + *element_type = RemangleAsType(dem, elem_node, flavor).GetCanonicalType(); if (is_incomplete) *is_incomplete = true; @@ -2882,6 +2909,10 @@ TypeSystemSwiftTypeRef::GetFunctionArgumentAtIndex(opaque_compiler_type_t type, const size_t index) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; + + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalOutermostType(dem, type); if (!node || (node->getKind() != Node::Kind::FunctionType && @@ -2894,7 +2925,7 @@ TypeSystemSwiftTypeRef::GetFunctionArgumentAtIndex(opaque_compiler_type_t type, if (num_args == index) for (NodePointer type : *child) if (type->getKind() == Node::Kind::Type) - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); ++num_args; } if (child->getKind() == Node::Kind::ArgumentTuple && @@ -2909,7 +2940,7 @@ TypeSystemSwiftTypeRef::GetFunctionArgumentAtIndex(opaque_compiler_type_t type, child->getKind() == Node::Kind::TupleElement) { NodePointer type = child->getFirstChild(); if (num_args == index && type->getKind() == Node::Kind::Type) - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); ++num_args; } } @@ -2939,6 +2970,9 @@ bool TypeSystemSwiftTypeRef::IsPossibleDynamicType(opaque_compiler_type_t type, if (!type) return false; + const char *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + auto impl = [&]() { using namespace swift::Demangle; Demangler dem; @@ -2948,7 +2982,7 @@ bool TypeSystemSwiftTypeRef::IsPossibleDynamicType(opaque_compiler_type_t type, return false; if (node->getKind() == Node::Kind::TypeAlias) { - auto resolved = ResolveTypeAlias(dem, node); + auto resolved = ResolveTypeAlias(dem, node, flavor); if (auto *n = std::get(resolved)) node = n; } @@ -3055,7 +3089,9 @@ ConstString TypeSystemSwiftTypeRef::GetTypeName(opaque_compiler_type_t type, Demangler dem; NodePointer print_node = GetDemangleTreeForPrinting(dem, AsMangledName(type), true); - auto mangling = mangleNode(print_node); + auto mangling = mangleNode( + print_node, + SwiftLanguageRuntime::GetManglingFlavor(AsMangledName(type))); std::string remangled; if (mangling.isSuccess()) remangled = mangling.result(); @@ -3077,10 +3113,11 @@ TypeSystemSwiftTypeRef::GetDisplayTypeName(opaque_compiler_type_t type, LLDB_SCOPED_TIMER(); auto impl = [&]() { using namespace swift::Demangle; + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(AsMangledName(type)); Demangler dem; NodePointer print_node = GetDemangleTreeForPrinting(dem, AsMangledName(type), false); - auto mangling = mangleNode(print_node); + auto mangling = mangleNode(print_node, flavor); std::string remangled; if (mangling.isSuccess()) remangled = mangling.result(); @@ -3102,9 +3139,12 @@ uint32_t TypeSystemSwiftTypeRef::GetTypeInfo( auto impl = [&]() { using namespace swift::Demangle; Demangler dem; - NodePointer node = dem.demangleSymbol(AsMangledName(type)); + const char *mangled_name = AsMangledName(type); + NodePointer node = dem.demangleSymbol(mangled_name); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + bool unresolved_typealias = false; - uint32_t flags = CollectTypeInfo(dem, node, unresolved_typealias); + uint32_t flags = CollectTypeInfo(dem, node, flavor, unresolved_typealias); if (unresolved_typealias) if (auto target_sp = GetTargetWP().lock()) if (auto swift_ast_ctx = GetSwiftASTContext( @@ -3177,7 +3217,8 @@ TypeSystemSwiftTypeRef::GetCanonicalType(opaque_compiler_type_t type) { ReconstructType({weak_from_this(), type}, nullptr).GetCanonicalType(); return GetTypeFromMangledTypename(ast_type.GetMangledTypeName()); } - auto mangling = mangleNode(canonical); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(AsMangledName(type)); + auto mangling = mangleNode(canonical, flavor); if (!mangling.isSuccess()) return CompilerType(); ConstString mangled(mangling.result()); @@ -3202,6 +3243,10 @@ CompilerType TypeSystemSwiftTypeRef::GetFunctionReturnType(opaque_compiler_type_t type) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; + + const char *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalOutermostType(dem, type); if (!node || (node->getKind() != Node::Kind::FunctionType && @@ -3212,20 +3257,20 @@ TypeSystemSwiftTypeRef::GetFunctionReturnType(opaque_compiler_type_t type) { if (child->getKind() == Node::Kind::ImplResult) { for (NodePointer type : *child) if (type->getKind() == Node::Kind::Type) - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); } if (child->getKind() == Node::Kind::ReturnType && child->getNumChildren() == 1) { NodePointer type = child->getFirstChild(); if (type->getKind() == Node::Kind::Type) - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); } } // Else this is a void / "()" type. NodePointer type = dem.createNode(Node::Kind::Type); NodePointer tuple = dem.createNode(Node::Kind::Tuple); type->addChild(tuple, dem); - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, flavor); }; VALIDATE_AND_RETURN(impl, GetFunctionReturnType, type, g_no_exe_ctx, (ReconstructType(type))); @@ -3262,8 +3307,10 @@ CompilerType TypeSystemSwiftTypeRef::GetPointerType(opaque_compiler_type_t type) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; - Demangler dem; + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; // The type that will be wrapped in UnsafePointer. auto *pointee_type = GetDemangledType(dem, AsMangledName(type)); if (!pointee_type) @@ -3274,7 +3321,7 @@ TypeSystemSwiftTypeRef::GetPointerType(opaque_compiler_type_t type) { auto *bgs = GetPointerTo(dem, pointee_type); pointer_type->addChild(bgs, dem); - return RemangleAsType(dem, pointer_type); + return RemangleAsType(dem, pointer_type, flavor); }; VALIDATE_AND_RETURN(impl, GetPointerType, type, g_no_exe_ctx, (ReconstructType(type))); @@ -3298,7 +3345,7 @@ CompilerType TypeSystemSwiftTypeRef::GetVoidFunctionType() { rett->addChild(ret_ty, dem); NodePointer ret_tuple = dem.createNode(Node::Kind::Tuple); ret_ty->addChild(ret_tuple, dem); - return RemangleAsType(dem, type); + return RemangleAsType(dem, type, GetManglingFlavor()); } // Exploring the type @@ -3480,7 +3527,10 @@ lldb::Encoding TypeSystemSwiftTypeRef::GetEncoding(opaque_compiler_type_t type, case Node::Kind::Weak: { auto *referent_node = node->getFirstChild(); assert(referent_node->getKind() == Node::Kind::Type); - auto referent_type = RemangleAsType(dem, referent_node); + + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + auto referent_type = RemangleAsType(dem, referent_node, flavor); return referent_type.GetEncoding(count); } default: @@ -3646,7 +3696,7 @@ TypeSystemSwiftTypeRef::ConvertClangTypeToSwiftType(CompilerType clang_type) { swift::Demangle::Demangler dem; swift::Demangle::NodePointer node = GetClangTypeTypeNode(dem, clang_type); - return RemangleAsType(dem, node); + return RemangleAsType(dem, node, GetManglingFlavor()); } llvm::Expected @@ -3750,7 +3800,8 @@ TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex( child_is_base_class = false; child_is_deref_of_parent = false; language_flags = 0; - return RemangleAsType(dem, GetClangTypeTypeNode(dem, raw_value)); + return RemangleAsType(dem, GetClangTypeTypeNode(dem, raw_value), + GetManglingFlavor(exe_ctx)); } // Otherwise defer to TypeSystemClang. // @@ -3826,7 +3877,7 @@ TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex( break; } child_name = prefix + child_name; - return RemangleAsType(dem, node); + return RemangleAsType(dem, node, GetManglingFlavor(exe_ctx)); } } } @@ -4087,7 +4138,9 @@ TypeSystemSwiftTypeRef::GetAsClangTypeOrNull(lldb::opaque_compiler_type_t type, bool *is_imported) { using namespace swift::Demangle; Demangler dem; - NodePointer node = GetDemangledType(dem, AsMangledName(type)); + const char *mangled_name = AsMangledName(type); + NodePointer node = GetDemangledType(dem, mangled_name); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); // Directly resolve Clang typedefs into Clang types. Imported // type aliases that point to Clang type that are also Swift builtins, like // Swift.Int, otherwise would resolved to Swift types. @@ -4095,7 +4148,7 @@ TypeSystemSwiftTypeRef::GetAsClangTypeOrNull(lldb::opaque_compiler_type_t type, node->getNumChildren() == 2 && node->getChild(0)->hasText() && node->getChild(0)->getText() == swift::MANGLING_MODULE_OBJC && node->getChild(1)->hasText()) { - auto node_clangtype = ResolveTypeAlias(dem, node, + auto node_clangtype = ResolveTypeAlias(dem, node, flavor, /*prefer_clang_types*/ true); if (node_clangtype.second) return node_clangtype.second; @@ -4168,7 +4221,7 @@ CompilerType TypeSystemSwiftTypeRef::GetRawPointerType() { swift::BUILTIN_TYPE_NAME_RAWPOINTER); NodePointer node = dem.createNode(Node::Kind::Type); node->addChild(raw_ptr, dem); - return RemangleAsType(dem, node); + return RemangleAsType(dem, node, GetManglingFlavor()); } bool TypeSystemSwiftTypeRef::IsErrorType(opaque_compiler_type_t type) { @@ -4223,7 +4276,7 @@ CompilerType TypeSystemSwiftTypeRef::GetErrorType() { dem); parent->addChild(dem.createNode(Node::Kind::Identifier, "Error"), dem); - return RemangleAsType(dem, error_type); + return RemangleAsType(dem, error_type, GetManglingFlavor()); }; VALIDATE_AND_RETURN_STATIC(impl, GetErrorType); } @@ -4232,6 +4285,9 @@ CompilerType TypeSystemSwiftTypeRef::GetReferentType(opaque_compiler_type_t type) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; + auto mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = GetDemangledType(dem, AsMangledName(type)); if (!node || @@ -4242,7 +4298,7 @@ TypeSystemSwiftTypeRef::GetReferentType(opaque_compiler_type_t type) { node = node->getFirstChild(); if (!node || node->getKind() != Node::Kind::Type || !node->hasChildren()) return {weak_from_this(), type}; - return RemangleAsType(dem, node); + return RemangleAsType(dem, node, flavor); }; VALIDATE_AND_RETURN(impl, GetReferentType, type, g_no_exe_ctx, (ReconstructType(type))); @@ -4270,11 +4326,14 @@ CompilerType TypeSystemSwiftTypeRef::GetStaticSelfType(lldb::opaque_compiler_type_t type) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; + auto mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = GetDemangledType(dem, AsMangledName(type)); auto *type_node = dem.createNode(Node::Kind::Type); type_node->addChild(GetStaticSelfType(dem, node), dem); - return RemangleAsType(dem, type_node); + return RemangleAsType(dem, type_node, flavor); }; VALIDATE_AND_RETURN(impl, GetStaticSelfType, type, g_no_exe_ctx, (ReconstructType(type))); @@ -4285,6 +4344,9 @@ TypeSystemSwiftTypeRef::GetInstanceType(opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; + auto mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalType(dem, type); @@ -4304,7 +4366,7 @@ TypeSystemSwiftTypeRef::GetInstanceType(opaque_compiler_type_t type, if (node->getKind() == Node::Kind::Metatype) { for (NodePointer child : *node) if (child->getKind() == Node::Kind::Type) - return RemangleAsType(dem, child); + return RemangleAsType(dem, child, flavor); return {}; } return {weak_from_this(), type}; @@ -4316,6 +4378,9 @@ TypeSystemSwiftTypeRef::GetInstanceType(opaque_compiler_type_t type, CompilerType TypeSystemSwiftTypeRef::CreateSILPackType(CompilerType type, bool indirect) { using namespace swift::Demangle; + auto mangled_name = type.GetMangledTypeName().GetStringRef(); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = GetDemangledType(dem, type.GetMangledTypeName().GetStringRef()); @@ -4326,7 +4391,7 @@ CompilerType TypeSystemSwiftTypeRef::CreateSILPackType(CompilerType type, pack_type->addChild(node, dem); NodePointer type_node = dem.createNode(Node::Kind::Type); type_node->addChild(pack_type, dem); - return RemangleAsType(dem, type_node); + return RemangleAsType(dem, type_node, flavor); } static std::optional @@ -4368,6 +4433,10 @@ TypeSystemSwiftTypeRef::IsSILPackType(CompilerType type) { CompilerType TypeSystemSwiftTypeRef::GetSILPackElementAtIndex(CompilerType type, unsigned i) { using namespace swift::Demangle; + + auto mangled_name = type.GetMangledTypeName().GetStringRef(); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node; if (auto Info = decodeSILPackType(dem, type, node)) { @@ -4379,7 +4448,7 @@ CompilerType TypeSystemSwiftTypeRef::GetSILPackElementAtIndex(CompilerType type, node = node->getFirstChild(); if (!node || node->getKind() != Node::Kind::Type) return {}; - return RemangleAsType(dem, node); + return RemangleAsType(dem, node, flavor); } return {}; } @@ -4392,6 +4461,11 @@ CompilerType TypeSystemSwiftTypeRef::CreateTupleType( auto *tuple_type = dem.createNode(Node::Kind::Type); auto *tuple = dem.createNode(Node::Kind::Tuple); tuple_type->addChild(tuple, dem); + if (elements.empty()) + return RemangleAsType(dem, tuple_type, GetManglingFlavor()); + + auto flavor = SwiftLanguageRuntime::GetManglingFlavor( + elements.front().element_type.GetMangledTypeName()); for (const auto &element : elements) { auto *tuple_element = dem.createNode(Node::Kind::TupleElement); @@ -4412,9 +4486,11 @@ CompilerType TypeSystemSwiftTypeRef::CreateTupleType( if (!element_type) return {}; type->addChild(element_type, dem); + assert(flavor == SwiftLanguageRuntime::GetManglingFlavor( + element.element_type.GetMangledTypeName())); } - return RemangleAsType(dem, tuple_type); + return RemangleAsType(dem, tuple_type, flavor); }; // The signature of VALIDATE_AND_RETURN doesn't support this function, below @@ -4563,10 +4639,12 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue( uint32_t bitfield_bit_offset, ExecutionContextScope *exe_scope, bool is_base_class) { LLDB_SCOPED_TIMER(); - auto impl = [&]() -> bool { - if (!type) - return false; + if (!type) + return false; + const char *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + auto impl = [&]() -> bool { using namespace swift::Demangle; Demangler dem; auto *node = DemangleCanonicalType(dem, type); @@ -4630,7 +4708,7 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue( case Node::Kind::Weak: { auto *referent_node = node->getFirstChild(); assert(referent_node->getKind() == Node::Kind::Type); - auto referent_type = RemangleAsType(dem, referent_node); + auto referent_type = RemangleAsType(dem, referent_node, flavor); return referent_type.DumpTypeValue( &s, format, data, data_offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset, exe_scope, is_base_class); @@ -4640,7 +4718,7 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue( case Node::Kind::Structure: { // In some instances, a swift `structure` wraps an objc enum. The enum // case needs to be handled, but structs are no-ops. - auto resolved = ResolveTypeAlias(dem, node, true); + auto resolved = ResolveTypeAlias(dem, node, flavor, true); auto clang_type = std::get(resolved); if (!clang_type) return false; @@ -4717,7 +4795,7 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue( Demangler dem; auto *node = DemangleCanonicalType(dem, type); bool unresolved_typealias = false; - CollectTypeInfo(dem, node, unresolved_typealias); + CollectTypeInfo(dem, node, flavor, unresolved_typealias); if (!node || unresolved_typealias) { if (auto swift_ast_ctx = GetSwiftASTContext(GetSymbolContext(exe_scope))) return swift_ast_ctx->DumpTypeValue( @@ -4893,11 +4971,14 @@ TypeSystemSwiftTypeRef::GetTypedefedType(opaque_compiler_type_t type) { auto impl = [&]() -> CompilerType { using namespace swift::Demangle; Demangler dem; - NodePointer node = GetDemangledType(dem, AsMangledName(type)); + const char *mangled_name = AsMangledName(type); + NodePointer node = GetDemangledType(dem, mangled_name); if (!node || (node->getKind() != Node::Kind::TypeAlias && node->getKind() != Node::Kind::BoundGenericTypeAlias)) return {}; - auto pair = ResolveTypeAlias(dem, node); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + + auto pair = ResolveTypeAlias(dem, node, flavor); NodePointer type_node = dem.createNode(Node::Kind::Type); if (NodePointer resolved = std::get(pair)) { type_node->addChild(resolved, dem); @@ -4908,7 +4989,7 @@ TypeSystemSwiftTypeRef::GetTypedefedType(opaque_compiler_type_t type) { return {}; type_node->addChild(clang_node, dem); } - return RemangleAsType(dem, type_node); + return RemangleAsType(dem, type_node, flavor); }; VALIDATE_AND_RETURN(impl, GetTypedefedType, type, g_no_exe_ctx, (ReconstructType(type))); @@ -4944,6 +5025,9 @@ bool TypeSystemSwiftTypeRef::IsReferenceType(opaque_compiler_type_t type, bool *is_rvalue) { auto impl = [&]() { using namespace swift::Demangle; + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalOutermostType(dem, type); if (!node || node->getNumChildren() != 1 || @@ -4952,9 +5036,9 @@ bool TypeSystemSwiftTypeRef::IsReferenceType(opaque_compiler_type_t type, if (pointee_type) { NodePointer referenced = node->getFirstChild(); - auto type = dem.createNode(Node::Kind::Type); + auto *type = dem.createNode(Node::Kind::Type); type->addChild(referenced, dem); - *pointee_type = RemangleAsType(dem, type); + *pointee_type = RemangleAsType(dem, type, flavor); } if (is_rvalue) @@ -4971,6 +5055,9 @@ CompilerType TypeSystemSwiftTypeRef::GetGenericArgumentType(opaque_compiler_type_t type, size_t idx) { auto impl = [&]() -> CompilerType { + const auto *mangled_name = AsMangledName(type); + auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_name); + Demangler dem; NodePointer node = DemangleCanonicalOutermostType(dem, type); if (!node || node->getNumChildren() != 2) @@ -4993,7 +5080,7 @@ TypeSystemSwiftTypeRef::GetGenericArgumentType(opaque_compiler_type_t type, if (!generic_argument_type) return {}; - return RemangleAsType(dem, generic_argument_type); + return RemangleAsType(dem, generic_argument_type, flavor); }; VALIDATE_AND_RETURN(impl, GetGenericArgumentType, type, g_no_exe_ctx, @@ -5034,6 +5121,16 @@ TypeSystemSwiftTypeRef::GetDependentGenericParamListForType( return dependent_params; } +swift::Mangle::ManglingFlavor +TypeSystemSwiftTypeRef::GetManglingFlavor(ExecutionContext *exe_ctx) { + auto sc = GetSymbolContext(exe_ctx); + if (auto ast_ctx = GetSwiftASTContext(sc)) + return ast_ctx->GetManglingFlavor(); + LLDB_LOG(GetLog(LLDBLog::Types), + "GetManglingFlavor failed to acquire a SwiftASTContext"); + return swift::Mangle::ManglingFlavor::Default; +} + #ifndef NDEBUG bool TypeSystemSwiftTypeRef::ShouldSkipValidation(opaque_compiler_type_t type) { auto mangled_name = GetMangledTypeName(type); diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h index 1b9303da79b37..ce49280eedec4 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h @@ -15,7 +15,10 @@ #include "Plugins/TypeSystem/Swift/TypeSystemSwift.h" #include "lldb/Core/SwiftForward.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/ThreadSafeDenseMap.h" +#include "swift/Demangling/ManglingFlavor.h" // FIXME: needed only for the DenseMap. #include "clang/APINotes/APINotesManager.h" @@ -332,8 +335,9 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { static bool IsBuiltinType(CompilerType type); /// Creates a GenericTypeParamType with the desired depth and index. - CompilerType CreateGenericTypeParamType(unsigned int depth, - unsigned int index) override; + CompilerType + CreateGenericTypeParamType(unsigned int depth, unsigned int index, + swift::Mangle::ManglingFlavor flavor) override; /// Create a __C imported struct type. CompilerType CreateClangStructType(llvm::StringRef name); @@ -400,7 +404,8 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { /// Wrap \p node as \p Global(TypeMangling(node)), remangle the type /// and create a CompilerType from it. CompilerType RemangleAsType(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node); + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor); /// Search the debug info for a non-nested Clang type with the specified name /// and cache the result. Users should prefer the version that takes in the @@ -425,6 +430,11 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { /// Lookup a type in the debug info. lldb::TypeSP FindTypeInModule(lldb::opaque_compiler_type_t type); + /// Returns the mangling flavor associated with the ASTContext corresponding + /// with this TypeSystem. + swift::Mangle::ManglingFlavor + GetManglingFlavor(ExecutionContext *exe_ctx = nullptr); + protected: /// Helper that creates an AST type from \p type. /// @@ -440,8 +450,10 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { /// Helper function that canonicalizes node, but doesn't look at its /// children. - swift::Demangle::NodePointer Canonicalize(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node); + swift::Demangle::NodePointer + Canonicalize(swift::Demangle::Demangler &dem, + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor); /// Demangle the mangled name of the canonical type of \p type and /// drill into the Global(TypeMangling(Type())). @@ -463,17 +475,17 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { /// If \p node is a Struct/Class/Typedef in the __C module, return a /// Swiftified node by looking up the name in the corresponding APINotes and /// optionally putting it into the correctly named module. - swift::Demangle::NodePointer GetSwiftified(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node, - bool resolve_objc_module); + swift::Demangle::NodePointer + GetSwiftified(swift::Demangle::Demangler &dem, + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool resolve_objc_module); /// Replace all "__C" module names with their actual Clang module /// names. This is the recursion step of \p /// GetDemangleTreeForPrinting(). Don't call it directly. - swift::Demangle::NodePointer - GetNodeForPrintingImpl(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node, - bool resolve_objc_module); + swift::Demangle::NodePointer GetNodeForPrintingImpl( + swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool resolve_objc_module); /// Return the demangle tree representation with all "__C" module /// names with their actual Clang module names. @@ -490,17 +502,18 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { CompilerType LookupClangForwardType(llvm::StringRef name, llvm::ArrayRef decl_context); - std::pair - ResolveTypeAlias(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node, - bool prefer_clang_types = false); + std::pair ResolveTypeAlias( + swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool prefer_clang_types = false); swift::Demangle::NodePointer GetCanonicalNode(swift::Demangle::Demangler &dem, - swift::Demangle::NodePointer node); + swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor); uint32_t CollectTypeInfo(swift::Demangle::Demangler &dem, swift::Demangle::NodePointer node, + swift::Mangle::ManglingFlavor flavor, bool &unresolved_typealias); swift::Demangle::NodePointer diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp index e2592feec74cf..166f111ef6220 100644 --- a/lldb/source/Symbol/CompileUnit.cpp +++ b/lldb/source/Symbol/CompileUnit.cpp @@ -213,11 +213,12 @@ VariableListSP CompileUnit::GetVariableList(bool can_create) { return m_variables; } -std::vector FindFileIndexes(const SupportFileList &files, - const FileSpec &file) { +std::vector +FindFileIndexes(const SupportFileList &files, const FileSpec &file, + RealpathPrefixes *realpath_prefixes = nullptr) { std::vector result; uint32_t idx = -1; - while ((idx = files.FindCompatibleIndex(idx + 1, file)) != + while ((idx = files.FindCompatibleIndex(idx + 1, file, realpath_prefixes)) != UINT32_MAX) result.push_back(idx); return result; @@ -247,7 +248,8 @@ uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, void CompileUnit::ResolveSymbolContext( const SourceLocationSpec &src_location_spec, - SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + SymbolContextItem resolve_scope, SymbolContextList &sc_list, + RealpathPrefixes *realpath_prefixes) { const FileSpec file_spec = src_location_spec.GetFileSpec(); const uint32_t line = src_location_spec.GetLine().value_or(LLDB_INVALID_LINE_NUMBER); @@ -278,8 +280,8 @@ void CompileUnit::ResolveSymbolContext( return; } - std::vector file_indexes = FindFileIndexes(GetSupportFiles(), - file_spec); + std::vector file_indexes = + FindFileIndexes(GetSupportFiles(), file_spec, realpath_prefixes); const size_t num_file_indexes = file_indexes.size(); if (num_file_indexes == 0) return; diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 261b9ab6d3e4d..0a9d4283fd455 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -323,6 +323,22 @@ bool CompilerType::IsBeingDefined() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool CompilerType::IsBoundsSafetyIndexable() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->IsBoundsSafetyIndexable(m_type); + return false; +} + +bool CompilerType::IsBoundsSafetyBidiIndexable() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->IsBoundsSafetyBidiIndexable(m_type); + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + bool CompilerType::IsInteger() const { bool is_signed = false; // May be reset by the call below. return IsIntegerType(is_signed); @@ -743,6 +759,29 @@ CompilerType CompilerType::AddRestrictModifier() const { return CompilerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +CompilerType CompilerType::AddBoundsSafetyIndexableAttribute() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->AddBoundsSafetyIndexableAttribute(m_type); + return CompilerType(); +} + +CompilerType CompilerType::AddBoundsSafetyBidiIndexableAttribute() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->AddBoundsSafetyBidiIndexableAttribute(m_type); + return CompilerType(); +} + +CompilerType CompilerType::AddBoundsSafetyUnspecifiedAttribute() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->AddBoundsSafetyUnspecifiedAttribute(m_type); + return CompilerType(); +} +/* TO_UPSTREAM(BoundsSafety) ON */ + CompilerType CompilerType::CreateTypedef(const char *name, const CompilerDeclContext &decl_ctx, uint32_t payload) const { diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp index 41b26e347b7f0..5f20f646bb159 100644 --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -366,6 +366,7 @@ AddressClass ObjectFile::GetAddressClass(addr_t file_addr) { case eSectionTypeDWARFAppleObjC: case eSectionTypeDWARFGNUDebugAltLink: case eSectionTypeCTF: + case eSectionTypeLLDBFormatters: case eSectionTypeLLDBTypeSummaries: case eSectionTypeSwiftModules: return AddressClass::eDebug; diff --git a/lldb/source/Symbol/SymbolFileOnDemand.cpp b/lldb/source/Symbol/SymbolFileOnDemand.cpp index 0cfe9fc1514b5..94979b2fb1c22 100644 --- a/lldb/source/Symbol/SymbolFileOnDemand.cpp +++ b/lldb/source/Symbol/SymbolFileOnDemand.cpp @@ -555,6 +555,12 @@ StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoIndexTime() { return m_sym_file_impl->GetDebugInfoIndexTime(); } +void SymbolFileOnDemand::ResetStatistics() { + LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(), + __FUNCTION__); + return m_sym_file_impl->ResetStatistics(); +} + void SymbolFileOnDemand::SetLoadDebugInfoEnabled() { if (m_debug_info_enabled) return; diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index 22f3c3c0e3bf4..3823ddde39c70 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -720,6 +720,21 @@ bool Type::ResolveCompilerType(ResolveState compiler_type_resolve_state) { } } + /* TO_UPSTREAM(BoundsSafety) ON */ + if (m_name.GetStringRef().starts_with("__bounds_safety::wide_ptr")) { + std::string name; + m_compiler_type = m_compiler_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr); + m_symbol_file->CompleteType(m_compiler_type); + + if (m_name.GetStringRef().contains("bidi_indexable")) + m_compiler_type = m_compiler_type.AddBoundsSafetyBidiIndexableAttribute(); + else + m_compiler_type = m_compiler_type.AddBoundsSafetyIndexableAttribute(); + + m_name = m_compiler_type.GetTypeName(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // If we have an encoding type, then we need to make sure it is resolved // appropriately. if (m_encoding_uid != LLDB_INVALID_UID) { diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp index e332a93bc4a42..5773188dce822 100644 --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -117,6 +117,23 @@ TypeSystem::AddRestrictModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +CompilerType +TypeSystem::AddBoundsSafetyIndexableAttribute(lldb::opaque_compiler_type_t type) { + return CompilerType(); +} + +CompilerType +TypeSystem::AddBoundsSafetyBidiIndexableAttribute(lldb::opaque_compiler_type_t type) { + return CompilerType(); +} + +CompilerType +TypeSystem::AddBoundsSafetyUnspecifiedAttribute(lldb::opaque_compiler_type_t type) { + return CompilerType(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + CompilerType TypeSystem::CreateTypedef(lldb::opaque_compiler_type_t type, const char *name, const CompilerDeclContext &decl_ctx, diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index 583d1524881fc..a5ddc1ff51b67 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -192,9 +192,33 @@ TargetStats::ToJSON(Target &target, } target_metrics_json.try_emplace("sourceMapDeduceCount", m_source_map_deduce_count); + target_metrics_json.try_emplace("sourceRealpathAttemptCount", + m_source_realpath_attempt_count); + target_metrics_json.try_emplace("sourceRealpathCompatibleCount", + m_source_realpath_compatible_count); + target_metrics_json.try_emplace("summaryProviderStatistics", + target.GetSummaryStatisticsCache().ToJSON()); return target_metrics_json; } +void TargetStats::Reset(Target &target) { + m_launch_or_attach_time.reset(); + m_first_private_stop_time.reset(); + m_first_public_stop_time.reset(); + // Report both the normal breakpoint list and the internal breakpoint list. + for (int i = 0; i < 2; ++i) { + BreakpointList &breakpoints = target.GetBreakpointList(i == 1); + std::unique_lock lock; + breakpoints.GetListMutex(lock); + size_t num_breakpoints = breakpoints.GetSize(); + for (size_t i = 0; i < num_breakpoints; i++) { + Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); + bp->ResetStatistics(); + } + } + target.GetSummaryStatisticsCache().Reset(); +} + void TargetStats::SetLaunchOrAttachTime() { m_launch_or_attach_time = StatsClock::now(); m_first_private_stop_time = std::nullopt; @@ -220,8 +244,38 @@ void TargetStats::IncreaseSourceMapDeduceCount() { ++m_source_map_deduce_count; } +void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count) { + m_source_realpath_attempt_count += count; +} + +void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) { + m_source_realpath_compatible_count += count; +} + bool DebuggerStats::g_collecting_stats = false; +void DebuggerStats::ResetStatistics(Debugger &debugger, Target *target) { + std::lock_guard guard( + Module::GetAllocationModuleCollectionMutex()); + const uint64_t num_modules = target != nullptr + ? target->GetImages().GetSize() + : Module::GetNumberAllocatedModules(); + for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { + Module *module = target != nullptr + ? target->GetImages().GetModuleAtIndex(image_idx).get() + : Module::GetAllocatedModuleAtIndex(image_idx); + if (module == nullptr) + continue; + module->ResetStatistics(); + } + if (target) + target->ResetStatistics(); + else { + for (const auto &target : debugger.GetTargetList().Targets()) + target->ResetStatistics(); + } +} + llvm::json::Value DebuggerStats::ReportStatistics( Debugger &debugger, Target *target, const lldb_private::StatisticsOptions &options) { @@ -247,14 +301,18 @@ llvm::json::Value DebuggerStats::ReportStatistics( std::vector modules; std::lock_guard guard( Module::GetAllocationModuleCollectionMutex()); - const uint64_t num_modules = Module::GetNumberAllocatedModules(); + const uint64_t num_modules = target != nullptr + ? target->GetImages().GetSize() + : Module::GetNumberAllocatedModules(); uint32_t num_debug_info_enabled_modules = 0; uint32_t num_modules_has_debug_info = 0; uint32_t num_modules_with_variable_errors = 0; uint32_t num_modules_with_incomplete_types = 0; uint32_t num_stripped_modules = 0; for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { - Module *module = Module::GetAllocatedModuleAtIndex(image_idx); + Module *module = target != nullptr + ? target->GetImages().GetModuleAtIndex(image_idx).get() + : Module::GetAllocatedModuleAtIndex(image_idx); ModuleStats module_stat; module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count(); module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count(); @@ -408,3 +466,26 @@ llvm::json::Value DebuggerStats::ReportStatistics( return std::move(global_stats); } + +llvm::json::Value SummaryStatistics::ToJSON() const { + return json::Object{{ + {"name", GetName()}, + {"type", GetSummaryKindName()}, + {"count", GetSummaryCount()}, + {"totalTime", GetTotalTime()}, + }}; +} + +json::Value SummaryStatisticsCache::ToJSON() { + std::lock_guard guard(m_map_mutex); + json::Array json_summary_stats; + for (const auto &summary_stat : m_summary_stats_map) + json_summary_stats.emplace_back(summary_stat.second->ToJSON()); + + return json_summary_stats; +} + +void SummaryStatisticsCache::Reset() { + for (const auto &summary_stat : m_summary_stats_map) + summary_stat.second->Reset(); +} diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 3d488dfc6459c..ff551291e4fdb 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -25,6 +25,7 @@ #include "lldb/Core/SourceManager.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormatterSection.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Expression/REPL.h" @@ -61,11 +62,10 @@ #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/RealpathPrefixes.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" -#include "lldb/ValueObject/ValueObject.h" -#include "lldb/ValueObject/ValueObjectConstResult.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SetVector.h" @@ -1486,94 +1486,6 @@ static void LoadScriptingResourceForModule(const ModuleSP &module_sp, feedback_stream.GetData()); } -// Load type summaries embedded in the binary. These are type summaries provided -// by the authors of the code. -static void LoadTypeSummariesForModule(ModuleSP module_sp) { - auto *sections = module_sp->GetSectionList(); - if (!sections) - return; - - auto summaries_sp = - sections->FindSectionByType(eSectionTypeLLDBTypeSummaries, true); - if (!summaries_sp) - return; - - Log *log = GetLog(LLDBLog::DataFormatters); - const char *module_name = module_sp->GetFileSpec().GetFilename().GetCString(); - - TypeCategoryImplSP category; - DataVisualization::Categories::GetCategory(ConstString("default"), category); - - // The type summary record is serialized as follows. - // - // Each record contains, in order: - // * Version number of the record format - // * The remaining size of the record - // * The size of the type identifier - // * The type identifier, either a type name, or a regex - // * The size of the summary string - // * The summary string - // - // Integers are encoded using ULEB. - // - // Strings are encoded with first a length (ULEB), then the string contents, - // and lastly a null terminator. The length includes the null. - - DataExtractor extractor; - auto section_size = summaries_sp->GetSectionData(extractor); - lldb::offset_t offset = 0; - while (offset < section_size) { - // Skip null bytes. Can happen with alignment padding. - while (true) { - auto next_offset = offset; - if (extractor.GetU8(&next_offset) != 0) { - break; - } - // Move past the null byte, using the advanced offset. - offset = next_offset; - } - - uint64_t version = extractor.GetULEB128(&offset); - uint64_t record_size = extractor.GetULEB128(&offset); - if (record_size == 0) { - LLDB_LOGF(log, - "Skipping empty (malformed) embedded type summary of version " - "%llu in %s.", - version, module_name); - continue; - } - - if (version == 1) { - uint64_t type_size = extractor.GetULEB128(&offset); - llvm::StringRef type_name = extractor.GetCStr(&offset, type_size); - uint64_t summary_size = extractor.GetULEB128(&offset); - llvm::StringRef summary_string = extractor.GetCStr(&offset, summary_size); - if (!type_name.empty() && !summary_string.empty()) { - TypeSummaryImpl::Flags flags; - auto summary_sp = - std::make_shared(flags, summary_string.data()); - FormatterMatchType match_type = eFormatterMatchExact; - if (type_name.front() == '^') - match_type = eFormatterMatchRegex; - category->AddTypeSummary(type_name, match_type, summary_sp); - LLDB_LOGF(log, "Loaded embedded type summary for '%s' from %s.", - type_name.data(), module_name); - } else { - if (type_name.empty()) - LLDB_LOGF(log, "Missing string(s) in embedded type summary in %s.", - module_name); - } - } else { - // Skip unsupported record. - offset += record_size; - LLDB_LOGF( - log, - "Skipping unsupported embedded type summary of version %llu in %s.", - version, module_name); - } - } -} - void Target::ClearModules(bool delete_locations) { ModulesDidUnload(m_images, delete_locations); m_section_load_history.Clear(); @@ -1855,6 +1767,7 @@ void Target::ModulesDidLoad(ModuleList &module_list) { ModuleSP module_sp(module_list.GetModuleAtIndex(idx)); LoadScriptingResourceForModule(module_sp, this); LoadTypeSummariesForModule(module_sp); + LoadFormattersForModule(module_sp); } m_breakpoint_list.UpdateBreakpoints(module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); @@ -3422,6 +3335,16 @@ bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp, void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } +lldb_private::SummaryStatisticsSP Target::GetSummaryStatisticsSPForProviderName( + lldb_private::TypeSummaryImpl &summary_provider) { + return m_summary_statistics_cache.GetSummaryStatisticsForProvider( + summary_provider); +} + +SummaryStatisticsCache &Target::GetSummaryStatisticsCache() { + return m_summary_statistics_cache; +} + void Target::SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info) { if (process_info.IsScriptedProcess()) { // Only copy scripted process launch options. @@ -4710,6 +4633,13 @@ InlineStrategy TargetProperties::GetInlineStrategy() const { static_cast(g_target_properties[idx].default_uint_value)); } +// Returning RealpathPrefixes, but the setting's type is FileSpecList. We do +// this because we want the FileSpecList to normalize the file paths for us. +RealpathPrefixes TargetProperties::GetSourceRealpathPrefixes() const { + const uint32_t idx = ePropertySourceRealpathPrefixes; + return RealpathPrefixes(GetPropertyAtIndexAs(idx, {})); +} + llvm::StringRef TargetProperties::GetArg0() const { const uint32_t idx = ePropertyArg0; return GetPropertyAtIndexAs( @@ -5371,3 +5301,5 @@ llvm::json::Value Target::ReportStatistics(const lldb_private::StatisticsOptions &options) { return m_stats.ToJSON(*this, options); } + +void Target::ResetStatistics() { m_stats.Reset(*this); } diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index fad46cc17fa49..f42a1a0e133ac 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -174,6 +174,9 @@ let Definition = "target" in { DefaultEnumValue<"eInlineBreakpointsAlways">, EnumValues<"OptionEnumValues(g_inline_breakpoint_enums)">, Desc<"The strategy to use when settings breakpoints by file and line. Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. Usually this is limited to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. Always checking for inlined breakpoint locations can be expensive (memory and time), so if you have a project with many headers and find that setting breakpoints is slow, then you can change this setting to headers. This setting allows you to control exactly which strategy is used when setting file and line breakpoints.">; + def SourceRealpathPrefixes: Property<"source-realpath-prefixes", "FileSpecList">, + DefaultStringValue<"">, + Desc<"Realpath any source paths that start with one of these prefixes. If the debug info contains symlinks which match the original source file's basename but don't match its location that the user will use to set breakpoints, then this setting can help resolve breakpoints correctly. This handles both symlinked files and directories. Wild card prefixes: An empty string matches all paths. A forward slash matches absolute paths.">; def DisassemblyFlavor: Property<"x86-disassembly-flavor", "Enum">, DefaultEnumValue<"eX86DisFlavorDefault">, EnumValues<"OptionEnumValues(g_x86_dis_flavor_value_types)">, diff --git a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp index d7f3d0925d2d4..34ce557533972 100644 --- a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp +++ b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp @@ -148,8 +148,11 @@ namespace lldb_private { void RegisterVerboseTrapFrameRecognizer(Process &process) { RegularExpressionSP module_regex_sp = nullptr; + // Older -fbounds-safety versions didn't have a ClangTrapPrefix, so the name + // of the frame was just the bounds-check failure message. This regex supports + // the old and new style of frames. auto symbol_regex_sp = std::make_shared( - llvm::formatv("^{0}", ClangTrapPrefix).str()); + llvm::formatv("^({0}|Bounds check failed|Pointer Check runtime failure)", ClangTrapPrefix).str()); StackFrameRecognizerSP srf_recognizer_sp = std::make_shared(); diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index c5825426c0719..6954a2508ffe1 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -52,6 +52,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES Log.cpp NameMatches.cpp ProcessInfo.cpp + RealpathPrefixes.cpp RegisterValue.cpp RegularExpression.cpp Instrumentation.cpp diff --git a/lldb/source/Utility/FileSpecList.cpp b/lldb/source/Utility/FileSpecList.cpp index 7647e04a82045..5852367f77827 100644 --- a/lldb/source/Utility/FileSpecList.cpp +++ b/lldb/source/Utility/FileSpecList.cpp @@ -7,7 +7,12 @@ //===----------------------------------------------------------------------===// #include "lldb/Utility/FileSpecList.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Target/Target.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RealpathPrefixes.h" #include "lldb/Utility/Stream.h" #include @@ -108,52 +113,85 @@ size_t SupportFileList::FindFileIndex(size_t start_idx, }); } -size_t SupportFileList::FindCompatibleIndex(size_t start_idx, - const FileSpec &file_spec) const { - const size_t num_files = m_files.size(); - if (start_idx >= num_files) - return UINT32_MAX; +enum IsCompatibleResult { + kNoMatch = 0, + kOnlyFileMatch = 1, + kBothDirectoryAndFileMatch = 2, +}; +IsCompatibleResult IsCompatible(const FileSpec &curr_file, + const FileSpec &file_spec) { const bool file_spec_relative = file_spec.IsRelative(); const bool file_spec_case_sensitive = file_spec.IsCaseSensitive(); // When looking for files, we will compare only the filename if the directory // argument is empty in file_spec const bool full = !file_spec.GetDirectory().IsEmpty(); + // Always start by matching the filename first + if (!curr_file.FileEquals(file_spec)) + return IsCompatibleResult::kNoMatch; + + // Only compare the full name if the we were asked to and if the current + // file entry has a directory. If it doesn't have a directory then we only + // compare the filename. + if (FileSpec::Equal(curr_file, file_spec, full)) { + return IsCompatibleResult::kBothDirectoryAndFileMatch; + } else if (curr_file.IsRelative() || file_spec_relative) { + llvm::StringRef curr_file_dir = curr_file.GetDirectory().GetStringRef(); + if (curr_file_dir.empty()) + // Basename match only for this file in the list + return IsCompatibleResult::kBothDirectoryAndFileMatch; + + // Check if we have a relative path in our file list, or if "file_spec" is + // relative, if so, check if either ends with the other. + llvm::StringRef file_spec_dir = file_spec.GetDirectory().GetStringRef(); + // We have a relative path in our file list, it matches if the + // specified path ends with this path, but we must ensure the full + // component matches (we don't want "foo/bar.cpp" to match "oo/bar.cpp"). + auto is_suffix = [](llvm::StringRef a, llvm::StringRef b, + bool case_sensitive) -> bool { + if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) + return a.empty() || a.ends_with("/"); + return false; + }; + const bool case_sensitive = + file_spec_case_sensitive || curr_file.IsCaseSensitive(); + if (is_suffix(curr_file_dir, file_spec_dir, case_sensitive) || + is_suffix(file_spec_dir, curr_file_dir, case_sensitive)) + return IsCompatibleResult::kBothDirectoryAndFileMatch; + } + return IsCompatibleResult::kOnlyFileMatch; +} + +size_t SupportFileList::FindCompatibleIndex( + size_t start_idx, const FileSpec &file_spec, + RealpathPrefixes *realpath_prefixes) const { + const size_t num_files = m_files.size(); + if (start_idx >= num_files) + return UINT32_MAX; + for (size_t idx = start_idx; idx < num_files; ++idx) { const FileSpec &curr_file = m_files[idx]->GetSpecOnly(); - // Always start by matching the filename first - if (!curr_file.FileEquals(file_spec)) - continue; - - // Only compare the full name if the we were asked to and if the current - // file entry has the a directory. If it doesn't have a directory then we - // only compare the filename. - if (FileSpec::Equal(curr_file, file_spec, full)) { + IsCompatibleResult result = IsCompatible(curr_file, file_spec); + if (result == IsCompatibleResult::kBothDirectoryAndFileMatch) return idx; - } else if (curr_file.IsRelative() || file_spec_relative) { - llvm::StringRef curr_file_dir = curr_file.GetDirectory().GetStringRef(); - if (curr_file_dir.empty()) - return idx; // Basename match only for this file in the list - - // Check if we have a relative path in our file list, or if "file_spec" is - // relative, if so, check if either ends with the other. - llvm::StringRef file_spec_dir = file_spec.GetDirectory().GetStringRef(); - // We have a relative path in our file list, it matches if the - // specified path ends with this path, but we must ensure the full - // component matches (we don't want "foo/bar.cpp" to match "oo/bar.cpp"). - auto is_suffix = [](llvm::StringRef a, llvm::StringRef b, - bool case_sensitive) -> bool { - if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) - return a.empty() || a.ends_with("/"); - return false; - }; - const bool case_sensitive = - file_spec_case_sensitive || curr_file.IsCaseSensitive(); - if (is_suffix(curr_file_dir, file_spec_dir, case_sensitive) || - is_suffix(file_spec_dir, curr_file_dir, case_sensitive)) - return idx; + + if (realpath_prefixes && result == IsCompatibleResult::kOnlyFileMatch) { + if (std::optional resolved_curr_file = + realpath_prefixes->ResolveSymlinks(curr_file)) { + if (IsCompatible(*resolved_curr_file, file_spec) == + IsCompatibleResult::kBothDirectoryAndFileMatch) { + // Stats and logging. + realpath_prefixes->IncreaseSourceRealpathCompatibleCount(); + Log *log = GetLog(LLDBLog::Source); + LLDB_LOGF(log, + "Realpath'ed support file %s is compatible to input file", + resolved_curr_file->GetPath().c_str()); + // We found a match + return idx; + } + } } } diff --git a/lldb/source/Utility/RealpathPrefixes.cpp b/lldb/source/Utility/RealpathPrefixes.cpp new file mode 100644 index 0000000000000..f6e1d17e30c69 --- /dev/null +++ b/lldb/source/Utility/RealpathPrefixes.cpp @@ -0,0 +1,71 @@ +//===-- RealpathPrefixes.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/RealpathPrefixes.h" + +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-private-types.h" + +using namespace lldb_private; + +RealpathPrefixes::RealpathPrefixes( + const FileSpecList &file_spec_list, + llvm::IntrusiveRefCntPtr fs) + : m_fs(fs) { + m_prefixes.reserve(file_spec_list.GetSize()); + for (const FileSpec &file_spec : file_spec_list) { + m_prefixes.emplace_back(file_spec.GetPath()); + } +} + +std::optional +RealpathPrefixes::ResolveSymlinks(const FileSpec &file_spec) { + if (m_prefixes.empty()) + return std::nullopt; + + // Test if `b` is a *path* prefix of `a` (not just *string* prefix). + // E.g. "/foo/bar" is a path prefix of "/foo/bar/baz" but not "/foo/barbaz". + auto is_path_prefix = [](llvm::StringRef a, llvm::StringRef b, + bool case_sensitive, + llvm::sys::path::Style style) -> bool { + if (case_sensitive ? a.consume_front(b) : a.consume_front_insensitive(b)) + // If `b` isn't "/", then it won't end with "/" because it comes from + // `FileSpec`. After `a` consumes `b`, `a` should either be empty (i.e. + // `a` == `b`) or end with "/" (the remainder of `a` is a subdirectory). + return b == "/" || a.empty() || + llvm::sys::path::is_separator(a[0], style); + return false; + }; + std::string file_spec_path = file_spec.GetPath(); + for (const std::string &prefix : m_prefixes) { + if (is_path_prefix(file_spec_path, prefix, file_spec.IsCaseSensitive(), + file_spec.GetPathStyle())) { + // Stats and logging. + IncreaseSourceRealpathAttemptCount(); + Log *log = GetLog(LLDBLog::Source); + LLDB_LOGF(log, "Realpath'ing support file %s", file_spec_path.c_str()); + + // One prefix matched. Try to realpath. + PathSmallString buff; + std::error_code ec = m_fs->getRealPath(file_spec_path, buff); + if (ec) + return std::nullopt; + FileSpec realpath(buff, file_spec.GetPathStyle()); + + // Only return realpath if it is different from the original file_spec. + if (realpath != file_spec) + return realpath; + return std::nullopt; + } + } + // No prefix matched + return std::nullopt; +} diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index 48299c8d0e394..7f5eb5b5c4075 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -680,7 +680,17 @@ bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr, m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on // the synthetic children being // up-to-date (e.g. ${svar%#}) - summary_ptr->FormatObject(this, destination, actual_options); + + if (TargetSP target_sp = GetExecutionContextRef().GetTargetSP()) { + SummaryStatisticsSP stats_sp = + target_sp->GetSummaryStatisticsCache() + .GetSummaryStatisticsForProvider(*summary_ptr); + + // Construct RAII types to time and collect data on summary creation. + SummaryStatistics::SummaryInvocation invocation(stats_sp); + summary_ptr->FormatObject(this, destination, actual_options); + } else + summary_ptr->FormatObject(this, destination, actual_options); } m_flags.m_is_getting_summary = false; return !destination.empty(); diff --git a/lldb/test/API/CMakeLists.txt b/lldb/test/API/CMakeLists.txt index 2b77531a13f6c..30a9b776fc3c4 100644 --- a/lldb/test/API/CMakeLists.txt +++ b/lldb/test/API/CMakeLists.txt @@ -65,6 +65,20 @@ set(LLDB_DEFAULT_TEST_EXECUTABLE "${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb${CMAKE_EXEC set(LLDB_DEFAULT_TEST_DSYMUTIL "${LLVM_TOOLS_BINARY_DIR}/dsymutil${CMAKE_EXECUTABLE_SUFFIX}") +if(LLDB_TEST_MAKE) + set(LLDB_DEFAULT_TEST_MAKE ${LLDB_TEST_MAKE}) +else() + find_program(LLDB_DEFAULT_TEST_MAKE make gmake) + if(LLDB_DEFAULT_TEST_MAKE) + message(STATUS "Found make: ${LLDB_DEFAULT_TEST_MAKE}") + else() + message(STATUS "Not found: make") + message(WARNING + "Many LLDB API tests require 'make' tool. Please provide it in Path " + "or pass via LLDB_TEST_MAKE.") + endif() +endif() + if (TARGET clang) set(LLDB_DEFAULT_TEST_COMPILER "${LLVM_TOOLS_BINARY_DIR}/clang${CMAKE_EXECUTABLE_SUFFIX}") else() @@ -74,6 +88,7 @@ endif() set(LLDB_TEST_EXECUTABLE "${LLDB_DEFAULT_TEST_EXECUTABLE}" CACHE PATH "lldb executable used for testing") set(LLDB_TEST_COMPILER "${LLDB_DEFAULT_TEST_COMPILER}" CACHE PATH "C Compiler to use for building LLDB test inferiors") set(LLDB_TEST_DSYMUTIL "${LLDB_DEFAULT_TEST_DSYMUTIL}" CACHE PATH "dsymutil used for generating dSYM bundles") +set(LLDB_TEST_MAKE "${LLDB_DEFAULT_TEST_MAKE}" CACHE PATH "make tool used for building test executables") if ("${LLDB_TEST_COMPILER}" STREQUAL "") message(FATAL_ERROR "LLDB test compiler not specified. Tests will not run.") diff --git a/lldb/test/API/commands/expression/top-level/Makefile b/lldb/test/API/commands/expression/top-level/Makefile index e5e9e78d4ead2..51b27ddbb3c2f 100644 --- a/lldb/test/API/commands/expression/top-level/Makefile +++ b/lldb/test/API/commands/expression/top-level/Makefile @@ -5,6 +5,6 @@ all: dummy include Makefile.rules dummy: dummy.cpp - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ CXX_SOURCES=dummy.cpp EXE=dummy diff --git a/lldb/test/API/commands/expression/weak_symbols/Makefile b/lldb/test/API/commands/expression/weak_symbols/Makefile index 6fd8133312ade..1636e9b303263 100644 --- a/lldb/test/API/commands/expression/weak_symbols/Makefile +++ b/lldb/test/API/commands/expression/weak_symbols/Makefile @@ -9,12 +9,12 @@ a.out: libdylib.dylib include Makefile.rules libdylib.dylib: dylib.c - $(MAKE) -C $(BUILDDIR) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -C $(BUILDDIR) -f $(MAKEFILE_RULES) \ C_SOURCES= DYLIB_C_SOURCES=dylib.c DYLIB_NAME=dylib \ CFLAGS_EXTRAS=-DHAS_THEM LD_EXTRAS=-dynamiclib hidden/libdylib.dylib: mkdir hidden - $(MAKE) -C $(BUILDDIR)/hidden -f $(MAKEFILE_RULES) \ + "$(MAKE)" -C $(BUILDDIR)/hidden -f $(MAKEFILE_RULES) \ C_SOURCES= DYLIB_C_SOURCES=dylib.c DYLIB_NAME=dylib \ LD_EXTRAS=-dynamiclib diff --git a/lldb/test/API/commands/statistics/basic/BoxFormatter.py b/lldb/test/API/commands/statistics/basic/BoxFormatter.py new file mode 100644 index 0000000000000..07681b32dfd09 --- /dev/null +++ b/lldb/test/API/commands/statistics/basic/BoxFormatter.py @@ -0,0 +1,15 @@ +import lldb + + +def summary(valobj, dict): + return f"[{valobj.GetChildAtIndex(0).GetValue()}]" + + +def __lldb_init_module(debugger, dict): + typeName = "Box<.*$" + debugger.HandleCommand( + 'type summary add -x "' + + typeName + + '" --python-function ' + + f"{__name__}.summary" + ) diff --git a/lldb/test/API/commands/statistics/basic/Makefile b/lldb/test/API/commands/statistics/basic/Makefile index c9319d6e6888a..3d0b98f13f3d7 100644 --- a/lldb/test/API/commands/statistics/basic/Makefile +++ b/lldb/test/API/commands/statistics/basic/Makefile @@ -1,2 +1,2 @@ -C_SOURCES := main.c +CXX_SOURCES := main.cpp include Makefile.rules diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py index a8ac60090a760..ec59652893a1e 100644 --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -1,7 +1,8 @@ -import lldb import json import os import re + +import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @@ -81,7 +82,7 @@ def get_command_stats(self, debug_stats): def test_expressions_frame_var_counts(self): self.build() lldbutil.run_to_source_breakpoint( - self, "// break here", lldb.SBFileSpec("main.c") + self, "// break here", lldb.SBFileSpec("main.cpp") ) self.expect("expr patatino", substrs=["27"]) @@ -224,7 +225,7 @@ def test_default_with_run(self): self.build() target = self.createTestTarget() lldbutil.run_to_source_breakpoint( - self, "// break here", lldb.SBFileSpec("main.c") + self, "// break here", lldb.SBFileSpec("main.cpp") ) debug_stats = self.get_stats() debug_stat_keys = [ @@ -250,6 +251,7 @@ def test_default_with_run(self): "launchOrAttachTime", "moduleIdentifiers", "targetCreateTime", + "summaryProviderStatistics", ] self.verify_keys(stats, '"stats"', keys_exist, None) self.assertGreater(stats["firstStopTime"], 0.0) @@ -447,6 +449,7 @@ def test_breakpoints(self): "targetCreateTime", "moduleIdentifiers", "totalBreakpointResolveTime", + "summaryProviderStatistics", ] self.verify_keys(target_stats, '"stats"', keys_exist, None) self.assertGreater(target_stats["totalBreakpointResolveTime"], 0.0) @@ -538,7 +541,7 @@ def test_no_dsym_binary_has_symfile_identifiers_in_stats(self): # in the stats. self.runCmd("b main.cpp:7") - debug_stats = self.get_stats() + debug_stats = self.get_stats("--all-targets") exe_stats = self.find_module_in_metrics(exe, debug_stats) # If we don't have a dSYM file, there should not be a key/value pair in @@ -918,3 +921,102 @@ def test_order_of_options_do_not_matter(self): debug_stats_1, f"The order of options '{options[0]}' and '{options[1]}' should not matter", ) + + def test_summary_statistics_providers(self): + """ + Test summary timing statistics is included in statistics dump when + a type with a summary provider exists, and is evaluated. + """ + + self.build() + target = self.createTestTarget() + lldbutil.run_to_source_breakpoint( + self, "// stop here", lldb.SBFileSpec("main.cpp") + ) + self.expect("frame var", substrs=["hello world"]) + stats = self.get_target_stats(self.get_stats()) + self.assertIn("summaryProviderStatistics", stats) + summary_providers = stats["summaryProviderStatistics"] + # We don't want to take a dependency on the type name, so we just look + # for string and that it was called once. + summary_provider_str = str(summary_providers) + self.assertIn("string", summary_provider_str) + self.assertIn("'count': 1", summary_provider_str) + self.assertIn("'totalTime':", summary_provider_str) + # We may hit the std::string C++ provider, or a summary provider string + self.assertIn("'type':", summary_provider_str) + self.assertTrue( + "c++" in summary_provider_str or "string" in summary_provider_str + ) + + self.runCmd("continue") + self.runCmd("command script import BoxFormatter.py") + self.expect("frame var", substrs=["box = [27]"]) + stats = self.get_target_stats(self.get_stats()) + self.assertIn("summaryProviderStatistics", stats) + summary_providers = stats["summaryProviderStatistics"] + summary_provider_str = str(summary_providers) + self.assertIn("BoxFormatter.summary", summary_provider_str) + self.assertIn("'count': 1", summary_provider_str) + self.assertIn("'totalTime':", summary_provider_str) + self.assertIn("'type': 'python'", summary_provider_str) + + def test_summary_statistics_providers_vec(self): + """ + Test summary timing statistics is included in statistics dump when + a type with a summary provider exists, and is evaluated. This variation + tests that vector recurses into it's child type. + """ + self.build() + target = self.createTestTarget() + lldbutil.run_to_source_breakpoint( + self, "// stop vector", lldb.SBFileSpec("main.cpp") + ) + self.expect( + "frame var", substrs=["int_vec", "double_vec", "[0] = 1", "[7] = 8"] + ) + stats = self.get_target_stats(self.get_stats()) + self.assertIn("summaryProviderStatistics", stats) + summary_providers = stats["summaryProviderStatistics"] + summary_provider_str = str(summary_providers) + self.assertIn("'count': 2", summary_provider_str) + self.assertIn("'totalTime':", summary_provider_str) + self.assertIn("'type':", summary_provider_str) + # We may hit the std::vector C++ provider, or a summary provider string + if "c++" in summary_provider_str: + self.assertIn("std::vector", summary_provider_str) + + @skipIfWindows + def test_multiple_targets(self): + """ + Test statistics dump only reports the stats from current target and + "statistics dump --all-targets" includes all target stats. + """ + da = {"CXX_SOURCES": "main.cpp", "EXE": self.getBuildArtifact("a.out")} + self.build(dictionary=da) + self.addTearDownCleanup(dictionary=da) + + db = {"CXX_SOURCES": "second.cpp", "EXE": self.getBuildArtifact("second.out")} + self.build(dictionary=db) + self.addTearDownCleanup(dictionary=db) + + main_exe = self.getBuildArtifact("a.out") + second_exe = self.getBuildArtifact("second.out") + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp"), None, "a.out" + ) + debugger_stats1 = self.get_stats() + self.assertIsNotNone(self.find_module_in_metrics(main_exe, debugger_stats1)) + self.assertIsNone(self.find_module_in_metrics(second_exe, debugger_stats1)) + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("second.cpp"), None, "second.out" + ) + debugger_stats2 = self.get_stats() + self.assertIsNone(self.find_module_in_metrics(main_exe, debugger_stats2)) + self.assertIsNotNone(self.find_module_in_metrics(second_exe, debugger_stats2)) + + all_targets_stats = self.get_stats("--all-targets") + self.assertIsNotNone(self.find_module_in_metrics(main_exe, all_targets_stats)) + self.assertIsNotNone(self.find_module_in_metrics(second_exe, all_targets_stats)) diff --git a/lldb/test/API/commands/statistics/basic/main.cpp b/lldb/test/API/commands/statistics/basic/main.cpp new file mode 100644 index 0000000000000..321d4b5034393 --- /dev/null +++ b/lldb/test/API/commands/statistics/basic/main.cpp @@ -0,0 +1,35 @@ +// Test that the lldb command `statistics` works. +#include +#include + +template class Box { + T m_value; + +public: + Box(T value) : m_value(value) {} +}; + +void foo() { + std::string str = "hello world"; + str += "\n"; // stop here +} + +void bar(int x) { + auto box = Box(x); + // stop here +} + +void vec() { + std::vector int_vec = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector double_vec = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}; + // stop vector + int x = int_vec.size(); +} + +int main(void) { + int patatino = 27; + foo(); + bar(patatino); + vec(); + return 0; // break here +} diff --git a/lldb/test/API/commands/statistics/basic/main.c b/lldb/test/API/commands/statistics/basic/second.cpp similarity index 81% rename from lldb/test/API/commands/statistics/basic/main.c rename to lldb/test/API/commands/statistics/basic/second.cpp index 2c5b4f5f098ee..3af4e320c2fb5 100644 --- a/lldb/test/API/commands/statistics/basic/main.c +++ b/lldb/test/API/commands/statistics/basic/second.cpp @@ -1,7 +1,5 @@ // Test that the lldb command `statistics` works. int main(void) { - int patatino = 27; - return 0; // break here } diff --git a/lldb/test/API/commands/target/create-deps/Makefile b/lldb/test/API/commands/target/create-deps/Makefile index 3e5b1049b5a19..866d550ee7d0a 100644 --- a/lldb/test/API/commands/target/create-deps/Makefile +++ b/lldb/test/API/commands/target/create-deps/Makefile @@ -6,5 +6,5 @@ a.out: libload_a include Makefile.rules libload_a: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=load_a DYLIB_CXX_SOURCES=a.cpp diff --git a/lldb/test/API/functionalities/breakpoint/break_in_loaded_dylib/Makefile b/lldb/test/API/functionalities/breakpoint/break_in_loaded_dylib/Makefile index 0f3fb37bdadf3..112210e7e2c60 100644 --- a/lldb/test/API/functionalities/breakpoint/break_in_loaded_dylib/Makefile +++ b/lldb/test/API/functionalities/breakpoint/break_in_loaded_dylib/Makefile @@ -2,7 +2,7 @@ CXX_SOURCES := main.cpp USE_LIBDL := 1 lib_b: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=lib_b all: lib_b diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/Makefile b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/TestBreakpoint.py b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/TestBreakpoint.py new file mode 100644 index 0000000000000..5dc2af73f3647 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/TestBreakpoint.py @@ -0,0 +1,158 @@ +""" +Test lldb breakpoint with symlinks/realpath and source-map. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil, lldbplatformutil + + +class BreakpointTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line_in_main = line_number("main.c", "// Set break point at this line.") + self.line_in_foo = line_number("real/foo.h", "// Set break point at this line.") + self.line_in_bar = line_number("real/bar.h", "// Set break point at this line.") + self.line_in_qux = line_number("real/qux.h", "// Set break point at this line.") + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def buildAndCreateTarget(self): + self.build() + exe = self.getBuildArtifact("a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + @skipIf(oslist=["windows"]) + def test_file_line_breakpoint_realpath_and_source_map(self): + """Test file/line breakpoint with realpathing and source-mapping.""" + self.buildAndCreateTarget() + cwd = os.getcwd() + + ###################################################################### + # Baseline + # -------------------------------------------------------------------- + # Breakpoints should be resolved with paths which are in the line-table. + lldbutil.run_break_set_by_file_and_line( + self, "main.c", self.line_in_main, num_expected_locations=1, loc_exact=True + ) + lldbutil.run_break_set_by_file_and_line( + self, + "symlink1/foo.h", + self.line_in_foo, + num_expected_locations=1, + loc_exact=True, + ) + lldbutil.run_break_set_by_file_and_line( + self, + "symlink2/bar.h", + self.line_in_bar, + num_expected_locations=1, + loc_exact=True, + ) + lldbutil.run_break_set_by_file_and_line( + self, + "symlink2/qux.h", + self.line_in_qux, + num_expected_locations=1, + loc_exact=True, + ) + + ###################################################################### + # Symlinked file + # -------------------------------------------------------------------- + # - `symlink1/foo.h` is a symlink file, pointing at `real/foo.h` + # - main.c includes `symlink1/foo.h`. + # - As a result, the line-table contains a support file `(test_base_dir)/symlink1/foo.h` + # - Setting a breakpoint for `real/foo.h` won't be resolved, because it doesn't match the above path. + # - Setting a realpath prefix to the current working directory will cause the above support file to be realpath'ed to `(test_base_dir)/real/foo.h` + # - Now setting a breakpoint for `real/foo.h` will be resolved. + lldbutil.run_break_set_by_file_and_line( + self, + "real/foo.h", + self.line_in_foo, + num_expected_locations=0, + loc_exact=True, + ) + self.runCmd(f'settings set target.source-realpath-prefixes "{cwd}"') + lldbutil.run_break_set_by_file_and_line( + self, + "real/foo.h", + self.line_in_foo, + num_expected_locations=1, + loc_exact=True, + ) + # Clear settings so that the test below won't be affected. + self.runCmd("settings clear target.source-realpath-prefixes") + + ###################################################################### + # Symlinked directory + # -------------------------------------------------------------------- + # - `symlink2` is a symlink directory, pointing at `real`. + # - So, `symlink2/bar.h` will be realpath'ed to `real/bar.h`. + # - main.c includes `symlink2/bar.h`. + # - As a result, the line-table contains a support file `(test_base_dir)/symlink2/bar.h` + # - Setting a breakpoint for `real/bar.h` won't be resolved, because it doesn't match the above path. + # - Setting a realpath prefix to the current working directory will cause the above support file to be realpath'ed to `(test_base_dir)/real/bar.h` + # - Now setting a breakpoint for `real/bar.h` will be resolved. + lldbutil.run_break_set_by_file_and_line( + self, + "real/bar.h", + self.line_in_foo, + num_expected_locations=0, + loc_exact=True, + ) + self.runCmd(f'settings set target.source-realpath-prefixes "{cwd}"') + lldbutil.run_break_set_by_file_and_line( + self, + "real/bar.h", + self.line_in_foo, + num_expected_locations=1, + loc_exact=True, + ) + # Clear settings so that the test below won't be affected. + self.runCmd("settings clear target.source-realpath-prefixes") + + ###################################################################### + # Symlink + source-map + # -------------------------------------------------------------------- + # - `symlink2` is a symlink directory, pointing at `real`. + # - So, `symlink2/qux.h` will be realpath'ed to `real/qux.h`. + # - main.c includes `symlink2/qux.h`. + # - As a result, the line-table contains a support file `(test_base_dir)/symlink2/qux.h` + # - Setting a realpath prefix to the current working directory will cause the above support file to be realpath'ed to `(test_base_dir)/real/qux.h` + # - Setting a breakpoint for `to-be-mapped/qux.h` won't be resolved, because it doesn't match the above path. + # - After setting a source-map, setting the same breakpoint will be resolved, because the input path `to-be-mapped/qux.h` is reverse-mapped to `real/qux.h`, which matches the realpath'ed support file. + lldbutil.run_break_set_by_file_and_line( + self, + "real/qux.h", + self.line_in_foo, + num_expected_locations=0, + loc_exact=True, + ) + self.runCmd(f'settings set target.source-realpath-prefixes "{cwd}"') + lldbutil.run_break_set_by_file_and_line( + self, + "to-be-mapped/qux.h", + self.line_in_foo, + num_expected_locations=0, + loc_exact=True, + ) + self.runCmd('settings set target.source-map "real" "to-be-mapped"') + lldbutil.run_break_set_by_file_and_line( + self, + "to-be-mapped/qux.h", + self.line_in_foo, + num_expected_locations=1, + loc_exact=True, + ) + # Clear settings so that the test below won't be affected. + self.runCmd("settings clear target.source-realpath-prefixes") diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/main.c b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/main.c new file mode 100644 index 0000000000000..716e7a91715f9 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/main.c @@ -0,0 +1,10 @@ +#include "symlink1/foo.h" +#include "symlink2/bar.h" +#include "symlink2/qux.h" + +int main(int argc, char const *argv[]) { + int a = foo(); // 1 + int b = bar(); // 2 + int c = qux(); // 3 + return a + b - c; // Set break point at this line. +} diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/bar.h b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/bar.h new file mode 100644 index 0000000000000..40155eb2075ab --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/bar.h @@ -0,0 +1,3 @@ +int bar() { + return 2; // Set break point at this line. +} diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/foo.h b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/foo.h new file mode 100644 index 0000000000000..a4ceefecf0123 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/foo.h @@ -0,0 +1,3 @@ +int foo() { + return 1; // Set break point at this line. +} diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/qux.h b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/qux.h new file mode 100644 index 0000000000000..a90e0feba30aa --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/real/qux.h @@ -0,0 +1,3 @@ +int qux() { + return 3; // Set break point at this line. +} diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink1/foo.h b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink1/foo.h new file mode 120000 index 0000000000000..c9001b4753bba --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink1/foo.h @@ -0,0 +1 @@ +../real/foo.h \ No newline at end of file diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink2 b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink2 new file mode 120000 index 0000000000000..ac558a3e1bf44 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/symlink2 @@ -0,0 +1 @@ +real \ No newline at end of file diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/to-be-mapped/README.md b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/to-be-mapped/README.md new file mode 100644 index 0000000000000..5067af8eed047 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_with_realpath_and_source_map/to-be-mapped/README.md @@ -0,0 +1 @@ +This is an empty folder just so that `settings set target.source-map "real" "to-be-mapped"` can be run successfully - it requires tha the latter path is valid. diff --git a/lldb/test/API/functionalities/completion/Makefile b/lldb/test/API/functionalities/completion/Makefile index f46742243bd96..e457693f62ca4 100644 --- a/lldb/test/API/functionalities/completion/Makefile +++ b/lldb/test/API/functionalities/completion/Makefile @@ -4,7 +4,7 @@ USE_LIBDL := 1 a.out: lib_shared lib_shared: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=shared.cpp DYLIB_NAME=shared include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-summary/Makefile b/lldb/test/API/functionalities/data-formatter/bytecode-summary/Makefile new file mode 100644 index 0000000000000..c9319d6e6888a --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/bytecode-summary/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := main.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-summary/TestBytecodeSummary.py b/lldb/test/API/functionalities/data-formatter/bytecode-summary/TestBytecodeSummary.py new file mode 100644 index 0000000000000..2b27bd3cdcda7 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/bytecode-summary/TestBytecodeSummary.py @@ -0,0 +1,17 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + @skipUnlessDarwin + def test(self): + self.build() + if self.TraceOn(): + self.expect("log enable -v lldb formatters") + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp") + ) + self.expect("v x", substrs=["(MyOptional) x = None"]) + self.expect("v y", substrs=["(MyOptional) y = 42"]) diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-summary/main.cpp b/lldb/test/API/functionalities/data-formatter/bytecode-summary/main.cpp new file mode 100644 index 0000000000000..35406acd6d0c4 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/bytecode-summary/main.cpp @@ -0,0 +1,43 @@ +// A bare-bones llvm::Optional reimplementation. + +template struct MyOptionalStorage { + MyOptionalStorage(T val) : value(val), hasVal(true) {} + MyOptionalStorage() {} + T value; + bool hasVal = false; +}; + +template struct MyOptional { + MyOptionalStorage Storage; + MyOptional(T val) : Storage(val) {} + MyOptional() {} + T &operator*() { return Storage.value; } +}; + +void stop() {} + +int main(int argc, char **argv) { + MyOptional x, y = 42; + stop(); // break here + return *y; +} + +// Produced from the assembler in +// Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/formatter.py +__attribute__((used, section("__DATA_CONST,__lldbformatters"))) unsigned char + _MyOptional_type_summary[] = + "\x01" // version + "\xa4" // record size + "\x01" // record size + "\x10" // type name size + "^MyOptional<.+>$" // type name + "\x00" // flags + "\x00" // sig_summary + "\x8d" // program size + "\x01" // program size + "\x1\x22\x7Storage#\x12\x60\x1,C\x10\x1\x5\x11\x2\x1\x22\x6hasVal#" + "\x12\x60\x1,\x10\x1e\x2\x22\x1b\x10G#!\x60 " + "\x0P\x10\x6\x22\x4None\x10\x36\x1#\x15\x60 " + "\x0#\x16\x60\x5\x22\x5value#\x12\x60\x5#\x17\x60\x1," + "\x10\x6\x22\x4None\x10\x11\x1#\x0\x60\x1#R\x60\x10\x3# " + "\x60\x10\x1\x2\x12\x12\x12\x12"; // summary function diff --git a/lldb/test/API/functionalities/data-formatter/embedded-summary/TestEmbeddedTypeSummary.py b/lldb/test/API/functionalities/data-formatter/embedded-summary/TestEmbeddedTypeSummary.py index 21b21ea760e76..2828e60dbe84b 100644 --- a/lldb/test/API/functionalities/data-formatter/embedded-summary/TestEmbeddedTypeSummary.py +++ b/lldb/test/API/functionalities/data-formatter/embedded-summary/TestEmbeddedTypeSummary.py @@ -11,3 +11,4 @@ def test(self): lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) self.expect("v player", substrs=['"Dirk" (41)']) self.expect("v layer", substrs=['"crust" (3)']) + diff --git a/lldb/test/API/functionalities/data-formatter/embedded-summary/main.c b/lldb/test/API/functionalities/data-formatter/embedded-summary/main.c index 313c4a84dde2f..d1757d1545ff7 100644 --- a/lldb/test/API/functionalities/data-formatter/embedded-summary/main.c +++ b/lldb/test/API/functionalities/data-formatter/embedded-summary/main.c @@ -1,4 +1,6 @@ -#include +void puts(const char *); + +#define LLDBSUMMARY __attribute__((section("__TEXT,__lldbsummaries"), used)) #define LLDBSUMMARY __attribute__((section("__TEXT,__lldbsummaries"), used)) @@ -20,6 +22,8 @@ struct Layer { int number; }; +LLDBSUMMARY unsigned char _padding[] = "\x00\x00"; + // Near copy of the record for `Player`, using a regex type name (`^Layer`). LLDBSUMMARY unsigned char _Layer_type_summary[] = "\x01" // version diff --git a/lldb/test/API/functionalities/dlopen_other_executable/Makefile b/lldb/test/API/functionalities/dlopen_other_executable/Makefile index 113b9fd7d3f18..51fc01bdde75a 100644 --- a/lldb/test/API/functionalities/dlopen_other_executable/Makefile +++ b/lldb/test/API/functionalities/dlopen_other_executable/Makefile @@ -2,7 +2,7 @@ C_SOURCES := main.c USE_LIBDL := 1 other: - $(MAKE) -f $(MAKEFILE_RULES) C_SOURCES=other.c EXE=other + "$(MAKE)" -f $(MAKEFILE_RULES) C_SOURCES=other.c EXE=other all: other include Makefile.rules diff --git a/lldb/test/API/functionalities/exec/Makefile b/lldb/test/API/functionalities/exec/Makefile index 8b9148ea8a355..65d4680077d26 100644 --- a/lldb/test/API/functionalities/exec/Makefile +++ b/lldb/test/API/functionalities/exec/Makefile @@ -5,5 +5,5 @@ all: secondprog include Makefile.rules secondprog: secondprog.cpp - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ CXX_SOURCES=secondprog.cpp EXE=secondprog diff --git a/lldb/test/API/functionalities/jitloader_gdb/Makefile b/lldb/test/API/functionalities/jitloader_gdb/Makefile index 357b1f83684fe..9998cc9cf833c 100644 --- a/lldb/test/API/functionalities/jitloader_gdb/Makefile +++ b/lldb/test/API/functionalities/jitloader_gdb/Makefile @@ -5,5 +5,5 @@ all: a.out simple include Makefile.rules simple: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ C_SOURCES=simple.c EXE=simple diff --git a/lldb/test/API/functionalities/limit-debug-info/Makefile b/lldb/test/API/functionalities/limit-debug-info/Makefile index 874b3a15e0fee..fa867a7aeb7c6 100644 --- a/lldb/test/API/functionalities/limit-debug-info/Makefile +++ b/lldb/test/API/functionalities/limit-debug-info/Makefile @@ -17,11 +17,11 @@ include Makefile.rules a.out: libone libtwo libone: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=one.cpp DYLIB_NAME=one \ CFLAGS_EXTRAS="$(ONE_CXXFLAGS)" libtwo: libone - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=two.cpp DYLIB_NAME=two \ CFLAGS_EXTRAS="$(TWO_CXXFLAGS)" LD_EXTRAS="-L. -lone" diff --git a/lldb/test/API/functionalities/load_after_attach/Makefile b/lldb/test/API/functionalities/load_after_attach/Makefile index 0f3fb37bdadf3..112210e7e2c60 100644 --- a/lldb/test/API/functionalities/load_after_attach/Makefile +++ b/lldb/test/API/functionalities/load_after_attach/Makefile @@ -2,7 +2,7 @@ CXX_SOURCES := main.cpp USE_LIBDL := 1 lib_b: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=lib_b all: lib_b diff --git a/lldb/test/API/functionalities/load_lazy/Makefile b/lldb/test/API/functionalities/load_lazy/Makefile index 81bc7dcb4d05f..8e1d06b1e39c3 100644 --- a/lldb/test/API/functionalities/load_lazy/Makefile +++ b/lldb/test/API/functionalities/load_lazy/Makefile @@ -17,13 +17,13 @@ else endif t1: t2_0 - $(MAKE) VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ + "$(MAKE)" VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=t1.c DYLIB_NAME=t1 LD_EXTRAS="-L. $(LINKFLAGS)" t2_0: - $(MAKE) VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ + "$(MAKE)" VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=t2_0.c DYLIB_NAME=t2_0 t2_1: - $(MAKE) VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ + "$(MAKE)" VPATH=$(SRCDIR) -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=t2_1.c DYLIB_NAME=t2_1 diff --git a/lldb/test/API/functionalities/load_unload/Makefile b/lldb/test/API/functionalities/load_unload/Makefile index e73ec73108764..dd7d16029427a 100644 --- a/lldb/test/API/functionalities/load_unload/Makefile +++ b/lldb/test/API/functionalities/load_unload/Makefile @@ -7,25 +7,25 @@ a.out: lib_b lib_a lib_c lib_d hidden_lib_d include Makefile.rules lib_a: lib_b - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=loadunload_a \ LD_EXTRAS="-L. -lloadunload_b" lib_b: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=loadunload_b lib_c: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=c.cpp DYLIB_NAME=loadunload_c lib_d: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=loadunload_d ifeq ($(OS),Darwin) install_name_tool -id @executable_path/libloadunload_d.dylib libloadunload_d.dylib endif hidden_lib_d: hidden - $(MAKE) VPATH=$(SRCDIR)/hidden -C hidden -f $(MAKEFILE_RULES) \ + "$(MAKE)" VPATH=$(SRCDIR)/hidden -C hidden -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=loadunload_d diff --git a/lldb/test/API/functionalities/load_using_paths/Makefile b/lldb/test/API/functionalities/load_using_paths/Makefile index 814a96013756a..f973a389d585f 100644 --- a/lldb/test/API/functionalities/load_using_paths/Makefile +++ b/lldb/test/API/functionalities/load_using_paths/Makefile @@ -6,6 +6,6 @@ all: hidden_lib a.out include Makefile.rules hidden_lib: - $(MAKE) VPATH=$(SRCDIR)/hidden -C hidden -f $(MAKEFILE_RULES) \ + "$(MAKE)" VPATH=$(SRCDIR)/hidden -C hidden -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=loadunload diff --git a/lldb/test/API/functionalities/scripted_process/Makefile b/lldb/test/API/functionalities/scripted_process/Makefile index ba739451fc7ef..d4f12fbb3c4ef 100644 --- a/lldb/test/API/functionalities/scripted_process/Makefile +++ b/lldb/test/API/functionalities/scripted_process/Makefile @@ -9,7 +9,7 @@ CXXFLAGS_EXTRAS := -target $(TRIPLE) all: libbaz.dylib a.out libbaz.dylib: baz.cpp - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=baz DYLIB_CXX_SOURCES=baz.cpp include Makefile.rules diff --git a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py index f06c9ae14bb7a..dbed130079fe3 100644 --- a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py +++ b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py @@ -120,6 +120,7 @@ def test_command_stats_api(self): self.assertNotIn("_regexp-bt", command_stats) @add_test_categories(["dwo"]) + @skipIf(bugnumber="rdar://141613283") def test_command_stats_force(self): """ Test reporting all pssible debug info stats by force loading all debug diff --git a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile index 4abcab84eac29..e4b0e86c0c36c 100644 --- a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile +++ b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile @@ -6,11 +6,11 @@ a.out: lib_a lib_b include Makefile.rules lib_a: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=load_a lib_b: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=load_b diff --git a/lldb/test/API/functionalities/tail_call_frames/cross_dso/Makefile b/lldb/test/API/functionalities/tail_call_frames/cross_dso/Makefile index 42c010be9a03a..963ce2ac94d92 100644 --- a/lldb/test/API/functionalities/tail_call_frames/cross_dso/Makefile +++ b/lldb/test/API/functionalities/tail_call_frames/cross_dso/Makefile @@ -10,4 +10,4 @@ a.out: lib_One lib_Two lib_One: lib_Two lib_%: - $(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/$*.mk DSYMUTIL=$(DSYMUTIL) + "$(MAKE)" VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/$*.mk DSYMUTIL=$(DSYMUTIL) diff --git a/lldb/test/API/functionalities/target-new-solib-notifications/Makefile b/lldb/test/API/functionalities/target-new-solib-notifications/Makefile index 6c61d210eeb2f..e3b48697fd783 100644 --- a/lldb/test/API/functionalities/target-new-solib-notifications/Makefile +++ b/lldb/test/API/functionalities/target-new-solib-notifications/Makefile @@ -1,23 +1,23 @@ CXX_SOURCES := main.cpp -LD_EXTRAS := -L. -l_d -l_c -l_a -l_b +LD_EXTRAS := -L. -l_d -l_c -l_a -l_b a.out: lib_b lib_a lib_c lib_d include Makefile.rules lib_a: lib_b - $(MAKE) -f $(MAKEFILE_RULES) \ - DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=_a \ - LD_EXTRAS="-L. -l_b" + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=_a \ + LD_EXTRAS="-L. -l_b" lib_b: - $(MAKE) -f $(MAKEFILE_RULES) \ - DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=_b + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=_b lib_c: - $(MAKE) -f $(MAKEFILE_RULES) \ - DYLIB_ONLY=YES DYLIB_CXX_SOURCES=c.cpp DYLIB_NAME=_c + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=c.cpp DYLIB_NAME=_c lib_d: - $(MAKE) -f $(MAKEFILE_RULES) \ - DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=_d + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=_d diff --git a/lldb/test/API/lang/BoundsSafety/array_of_ptrs/Makefile b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/array_of_ptrs/TestArrayOfBoundsSafetyPointers.py b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/TestArrayOfBoundsSafetyPointers.py new file mode 100644 index 0000000000000..0a1cfdd848643 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/TestArrayOfBoundsSafetyPointers.py @@ -0,0 +1,48 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestArrayOfBoundsSafetyPointers(TestBase): + def __run(self, build_dict): + self.build(dictionary = build_dict) + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here 1", lldb.SBFileSpec("main.c")) + + zero_init_pattern = '\(out-of-bounds ptr: 0x000000000000..0x000000000004, bounds: 0x000000000000..0x000000000000\)' + array_patterns = [f'\[0\] = {zero_init_pattern}', + f'\[1\] = {zero_init_pattern}'] + + self.expect("expr array_of_bounds_safety_pointers", patterns = array_patterns) + self.expect("frame variable array_of_bounds_safety_pointers", patterns = array_patterns) + + self.expect("expr array_of_bounds_safety_pointers[0]", patterns = [zero_init_pattern]) + self.expect("frame variable array_of_bounds_safety_pointers[0]", patterns = [zero_init_pattern]) + + self.expect("expr array_of_bounds_safety_pointers[1]", patterns = [zero_init_pattern]) + self.expect("frame variable array_of_bounds_safety_pointers[1]", patterns = [zero_init_pattern]) + + lldbutil.continue_to_source_breakpoint(self, self.process, "// break here 2", lldb.SBFileSpec("main.c")) + + initialized_pattern = '\(ptr: (0x[0-9a-f]*), bounds: \\1..0x[0-9a-f]*\)' + array_patterns = [f'\[0\] = {initialized_pattern}', + f'\[1\] = {zero_init_pattern}'] + + self.expect("expr array_of_bounds_safety_pointers", patterns = array_patterns) + self.expect("frame variable array_of_bounds_safety_pointers", patterns = array_patterns) + + self.expect("expr array_of_bounds_safety_pointers[0]", patterns = [initialized_pattern]) + self.expect("frame variable array_of_bounds_safety_pointers[0]", patterns = [initialized_pattern]) + + self.expect("expr array_of_bounds_safety_pointers[1]", patterns = [zero_init_pattern]) + self.expect("frame variable array_of_bounds_safety_pointers[1]", patterns = [zero_init_pattern]) + + @skipIf(bugnumber="rdar://141363609") + def test_optimized(self): + build_dict=dict(CFLAGS_EXTRAS="-O2 -Xclang -fbounds-safety") + self.__run(build_dict) + + @skipIf(bugnumber="rdar://141363609") + def test_unoptimized(self): + build_dict=dict(CFLAGS_EXTRAS="-Xclang -fbounds-safety") + self.__run(build_dict) diff --git a/lldb/test/API/lang/BoundsSafety/array_of_ptrs/main.c b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/main.c new file mode 100644 index 0000000000000..540062c69874b --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/array_of_ptrs/main.c @@ -0,0 +1,8 @@ +#include + +int *__bidi_indexable array_of_bounds_safety_pointers[2]; + +int main() { + array_of_bounds_safety_pointers[0] = (int *)malloc(16); // break here 1 + return 0; // break here 2 +} diff --git a/lldb/test/API/lang/BoundsSafety/basic_types/Makefile b/lldb/test/API/lang/BoundsSafety/basic_types/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/basic_types/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/basic_types/TestBasicTypes.py b/lldb/test/API/lang/BoundsSafety/basic_types/TestBasicTypes.py new file mode 100644 index 0000000000000..edec03b43c92f --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/basic_types/TestBasicTypes.py @@ -0,0 +1,43 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicTypes(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + + def test(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + for t, v in [("char", "arr"), + ("int", "arri"), + ("float", "arrf"), + ("double", "arrd"), + ("A", "arrA")]: + expected_c_str = '' + if t == 'char': + # When printing a char arr[] we print the C string that is pointed to + expected_c_str = r' *"hello world" *' + self.expect("expr i_"+v, + patterns = ['\('+t+' \*__indexable\) \$[0-9]+ = \(ptr: 0x[a-f0-9]+, upper bound: 0x[a-f0-9]+\)' + expected_c_str + '$']) + self.expect("expr bi_"+v, + patterns = ['\('+t+' \*__bidi_indexable\) \$[0-9]+ = \(ptr: 0x[a-f0-9]+, bounds: 0x[a-f0-9]+..0x[a-f0-9]+\)' + expected_c_str + '$']) + + biArg = self.frame().FindVariable("bi_"+v) + self.assertTrue(bool(re.match('0x[a-f0-9]+',biArg.GetValue()))) + + lldbutil.continue_to_breakpoint(self.process, bkpt); + + self.expect("expr arrp", + patterns = ['\(int \(\*__bidi_indexable\)\[8\]\) \$[0-9]+ = \(ptr: 0x[a-f0-9]+, bounds: 0x[a-f0-9]+..0x[a-f0-9]+\)$']) + + arrp = self.frame().FindVariable("arrp") + self.assertTrue(bool(re.match('0x[a-f0-9]+',arrp.GetValue()))) + + self.expect( "expr callback", + patterns = ['\(int \(\*\)\(int \*__bidi_indexable\)\) \$[0-9]+ = 0x[a-f0-9]+']) + diff --git a/lldb/test/API/lang/BoundsSafety/basic_types/main.c b/lldb/test/API/lang/BoundsSafety/basic_types/main.c new file mode 100644 index 0000000000000..69ab23214778a --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/basic_types/main.c @@ -0,0 +1,49 @@ +#include + +void *store; +void consume(void* p) { + store = p; +} + +struct A { int x; int y;}; + +int f1(int *__bidi_indexable arr) { return *arr; } + +int f(char *__indexable i_arr, char *__bidi_indexable bi_arr, + int *__indexable i_arri, int *__bidi_indexable bi_arri, + float *__indexable i_arrf, float *__bidi_indexable bi_arrf, + double *__indexable i_arrd, double *__bidi_indexable bi_arrd, + struct A *__indexable i_arrA, struct A *__bidi_indexable bi_arrA) { + // Calls to consume are to ensure the `__bidi_indexable` parameters + // have debug info emitted (rdar://102116986). + // We break here to workaround a bug where LLDB is unable to print + // the value of `bi_arr` after its last use (rdar://102149316). + consume(bi_arr); // break here + consume(bi_arri); + consume(bi_arrf); + consume(bi_arrd); + consume(bi_arrA); + + return i_arri[0]; +} + +int main() { + char arr[] = "hello world"; + int arri[] = {1,2,3}; + float arrf[] = {1.0f, 2.0f, 3.0f}; + double arrd[] = {1.0, 2.0, 3.0}; + struct A arrA[] = {{1,2}, {3,4}, {4,5}}; + int (*callback)(int *__bidi_indexable) = f1; + + f(arr, arr, + arri, arri, + arrf, arrf, + arrd, arrd, + arrA, arrA); + + int (*arrp)[8]; + int arre[8] = {1,2,3,4,5,6,7,8}; + arrp = &arre; + + return 0; // break here +} diff --git a/lldb/test/API/lang/BoundsSafety/bound_pointer/Makefile b/lldb/test/API/lang/BoundsSafety/bound_pointer/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/bound_pointer/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/bound_pointer/TestLocalPointerBound.py b/lldb/test/API/lang/BoundsSafety/bound_pointer/TestLocalPointerBound.py new file mode 100644 index 0000000000000..1f830a243e5a6 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/bound_pointer/TestLocalPointerBound.py @@ -0,0 +1,39 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestLocalPointerBound(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + self.build() + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + self.expect_expr("foo(5)", result_type="int *") + self.expect_expr("ptr1", result_type="int *__bidi_indexable") + self.expect_expr("ptr2", result_type="int *__bidi_indexable") + self.expect_expr("ps", result_type="S *__bidi_indexable") + + self.expect( "expr ptr1", + substrs=['(int *__bidi_indexable)', + '$4 =', + 'bounds:', + '..']) + + self.expect( "expr ptr2", + substrs=['(int *__bidi_indexable)', + '$5 =', + 'bounds:', + '..']) + + self.expect( "expr ps", + substrs=['(S *__bidi_indexable)', + '$6 =', + 'bounds:', + '..']) + + lldbutil.continue_to_breakpoint(self.process, bkpt); + + self.expect_expr("p", result_type="int *") diff --git a/lldb/test/API/lang/BoundsSafety/bound_pointer/main.c b/lldb/test/API/lang/BoundsSafety/bound_pointer/main.c new file mode 100644 index 0000000000000..8b88194fcb68f --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/bound_pointer/main.c @@ -0,0 +1,35 @@ +int arr[10]; +int v; + +struct S { + int i; + char *p; +}; + +int f(int *p) { + return *p; // break here +} + +int* foo(int count) { + int arr[10]; + int *local = &arr[0]; + for (int i = 0; i < count; ++i) + local[i] = i; + return local; +} + +int main() { + int x = *foo(5); + + int *ptr1 = &arr[v]; + int *ptr2 = arr; + + struct S s = {1, 0}; + struct S *ps = &s; + + f(ptr1); // break here + + ptr1++; + + return x; +} diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/Makefile b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/Makefile new file mode 100644 index 0000000000000..9bc8d96cc74e3 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/Makefile @@ -0,0 +1,10 @@ +C_SOURCES := \ + main.c \ + bidi_check_known_type_size.c \ + bidi_check_unknown_type_size.c \ + idx_check_known_type_size.c \ + idx_check_unknown_type_size.c + +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/TestOutOfBoundsPointer.py b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/TestOutOfBoundsPointer.py new file mode 100644 index 0000000000000..a157f34c44ecf --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/TestOutOfBoundsPointer.py @@ -0,0 +1,290 @@ +import lldb +import re +from lldbsuite.test.decorators import * +from lldbsuite.test.decorators import _get_bool_config +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from enum import Enum + + +class OOBKind(Enum): + Full = (0,) + Partial = (1,) + Overflow = (2,) + NotOOB = 3 + + +def OOBKindToStr(kind: OOBKind) -> str: + if kind is OOBKind.Full: + return "out-of-bounds" + if kind is OOBKind.Partial: + return "partially out-of-bounds" + if kind is OOBKind.Overflow: + return "overflown bounds" + if kind is OOBKind.NotOOB: + return "" + + +class TestOutOfBoundsPointer(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @staticmethod + def get_bidi_var_regex(oob_kind, type_name): + if oob_kind == OOBKind.NotOOB: + return ( + r"\({type_name} *__bidi_indexable\) [a-zA-Z_0-9]+ = " + "\(ptr: 0x[a-f0-9]+, bounds: 0x[a-f0-9]+..0x[a-f0-9]+\)" + ).format(type_name=re.escape(type_name)) + else: + oob = OOBKindToStr(oob_kind) + return ( + r"\({type_name} *__bidi_indexable\) [a-zA-Z_0-9]+ = " + "\({oob} ptr: 0x[a-f0-9]+..0x[a-f0-9]+, bounds: 0x[a-f0-9]+..0x[a-f0-9]+\)" + ).format(type_name=re.escape(type_name), oob=re.escape(oob)) + + @staticmethod + def get_idx_var_regex(oob_kind, type_name): + if oob_kind == OOBKind.NotOOB: + return ( + r"\({type_name} *__indexable\) [a-zA-Z_0-9]+ = " + "\(ptr: 0x[a-f0-9]+, upper bound: 0x[a-f0-9]+\)" + ).format(type_name=re.escape(type_name)) + else: + oob = OOBKindToStr(oob_kind) + return ( + r"\({type_name} *__indexable\) [a-zA-Z_0-9]+ = " + "\({oob} ptr: 0x[a-f0-9]+..0x[a-f0-9]+, upper bound: 0x[a-f0-9]+\)" + ).format(type_name=re.escape(type_name), oob=re.escape(oob)) + + def bidi_in_bounds(self, type_name): + return self.get_bidi_var_regex(oob_kind=OOBKind.NotOOB, type_name=type_name) + + def bidi_full_oob(self, type_name): + return self.get_bidi_var_regex(oob_kind=OOBKind.Full, type_name=type_name) + + def bidi_partial_oob(self, type_name): + return self.get_bidi_var_regex(oob_kind=OOBKind.Partial, type_name=type_name) + + def bidi_overflow_oob(self, type_name): + return self.get_bidi_var_regex(oob_kind=OOBKind.Overflow, type_name=type_name) + + def in_bounds(self, type_name): + return self.get_idx_var_regex(oob_kind=OOBKind.NotOOB, type_name=type_name) + + def full_oob(self, type_name): + return self.get_idx_var_regex(oob_kind=OOBKind.Full, type_name=type_name) + + def partial_oob(self, type_name): + return self.get_idx_var_regex(oob_kind=OOBKind.Partial, type_name=type_name) + + def overflow_oob(self, type_name): + return self.get_idx_var_regex(oob_kind=OOBKind.Overflow, type_name=type_name) + + @skipIf(bugnumber="rdar://141363609") + def test_bidi_known_type_size(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( + self, r"// break here:.+", lldb.SBFileSpec("bidi_check_known_type_size.c") + ) + + # ----------------------------------------------------------------------- + # ptr < lower bound + # ----------------------------------------------------------------------- + self.expect( + "frame variable oob_ptr_lower", patterns=[self.bidi_in_bounds("char *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_ptr_lower", patterns=[self.bidi_full_oob("char *")] + ) + + # ----------------------------------------------------------------------- + # ptr + type size overflows + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_ptr_plus_size_overflows", + patterns=[self.bidi_overflow_oob("int *")], + ) + + # ----------------------------------------------------------------------- + # ptr >= upper bound + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("char *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("char *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("char *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.bidi_full_oob("char *")]) + + # ----------------------------------------------------------------------- + # Upper bound not aligned with type + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper_type_pun", + patterns=[self.bidi_partial_oob("int *")], + ) + + # ----------------------------------------------------------------------- + # nullptr + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_null", patterns=[self.bidi_full_oob("int *")]) + + # ----------------------------------------------------------------------- + # Flexible array member + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams0", patterns=[self.bidi_in_bounds("FAMS_t *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams1", patterns=[self.bidi_in_bounds("FAMS_t *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams2", patterns=[self.bidi_full_oob("FAMS_t *")]) + + @skipIf(bugnumber="rdar://141363609") + def test_bidi_unknown_type_size(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( + self, r"// break here:.+", lldb.SBFileSpec("bidi_check_unknown_type_size.c") + ) + + # ----------------------------------------------------------------------- + # ptr < lower bound + # ----------------------------------------------------------------------- + self.expect( + "frame variable oob_ptr_lower", patterns=[self.bidi_in_bounds("void *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_ptr_lower", patterns=[self.bidi_full_oob("void *")] + ) + + # ----------------------------------------------------------------------- + # ptr + type size overflows + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_ptr_plus_size_overflows", + patterns=[self.bidi_overflow_oob("void *")], + ) + + # ----------------------------------------------------------------------- + # ptr >= upper bound + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("void *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("void *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper", patterns=[self.bidi_in_bounds("void *")] + ) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.bidi_full_oob("void *")]) + + # ----------------------------------------------------------------------- + # nullptr + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_null", patterns=[self.bidi_full_oob("void *")]) + + @skipIf(bugnumber="rdar://141363609") + def test_idx_known_type_size(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( + self, r"// break here:.+", lldb.SBFileSpec("idx_check_known_type_size.c") + ) + + # ----------------------------------------------------------------------- + # ptr + type size overflows + # ----------------------------------------------------------------------- + self.expect( + "frame variable oob_ptr_plus_size_overflows", + patterns=[self.overflow_oob("int *")], + ) + + # ----------------------------------------------------------------------- + # ptr > upper bound + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("char *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("char *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("char *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.full_oob("char *")]) + + # ----------------------------------------------------------------------- + # Upper bound not aligned with type + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect( + "frame variable oob_upper_type_pun", patterns=[self.partial_oob("int *")] + ) + + # ----------------------------------------------------------------------- + # nullptr + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_null", patterns=[self.full_oob("int *")]) + + # ----------------------------------------------------------------------- + # Flexible array member + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams0", patterns=[self.in_bounds("FAMS_t *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams1", patterns=[self.in_bounds("FAMS_t *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable fams2", patterns=[self.full_oob("FAMS_t *")]) + + @skipIf(bugnumber="rdar://141363609") + def test_idx_unknown_type_size(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( + self, r"// break here:.+", lldb.SBFileSpec("idx_check_unknown_type_size.c") + ) + + # ----------------------------------------------------------------------- + # ptr + type size overflows + # ----------------------------------------------------------------------- + self.expect( + "frame variable oob_ptr_plus_size_overflows", + patterns=[self.overflow_oob("void *")], + ) + + # ----------------------------------------------------------------------- + # ptr >= upper bound + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("void *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("void *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.in_bounds("void *")]) + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_upper", patterns=[self.full_oob("void *")]) + + # ----------------------------------------------------------------------- + # nullptr + # ----------------------------------------------------------------------- + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect("frame variable oob_null", patterns=[self.full_oob("void *")]) diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_known_type_size.c b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_known_type_size.c new file mode 100644 index 0000000000000..4e60456f28eb4 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_known_type_size.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +static void noop() {}; + +typedef struct FAMS { + int count; + int buffer[__counted_by(count)]; +} FAMS_t; + +static FAMS_t *__bidi_indexable +get_next_fam_struct(FAMS_t *__bidi_indexable current) { + uintptr_t current_size = sizeof(*current) + (sizeof(int) * current->count); + uintptr_t next = ((uintptr_t)current) + current_size; + uintptr_t num_bytes = + ((uintptr_t)__ptr_upper_bound(current)) - ((uintptr_t)current); + uintptr_t remaining_bytes = 0; + if (num_bytes > current_size) + remaining_bytes = num_bytes - current_size; + return __unsafe_forge_bidi_indexable(FAMS_t *, next, remaining_bytes); +} + +static FAMS_t *__bidi_indexable alloc_fams_buffer(size_t num_fams, + size_t num_elts_in_buffer) { + const size_t buffer_size = sizeof(int) * num_elts_in_buffer; + FAMS_t *__bidi_indexable fams = + malloc(num_fams * (sizeof(FAMS_t) + buffer_size)); + // Set the counts and zero init buffer + FAMS_t *__bidi_indexable current = fams; + for (size_t fam_num = 0; fam_num < num_fams; ++fam_num) { + current->count = num_elts_in_buffer; + // Don't zero-init due to rdar://102831737 + // memset(current->buffer, 0, buffer_size); + current = get_next_fam_struct(current); + } + return fams; +} + +void bidi_check_known_type_size() { + char data[] = "fo"; + //-------------------------------------------------------------------------- + // ptr < lower bound + //-------------------------------------------------------------------------- + char * __bidi_indexable oob_ptr_lower = data; // still in-bounds + oob_ptr_lower -= 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // ptr + type size overflows + //-------------------------------------------------------------------------- + int* __unsafe_indexable ptr_at_edge_of_addr_space = (int* __unsafe_indexable) UINTPTR_MAX; + int * __bidi_indexable oob_ptr_plus_size_overflows = __unsafe_forge_bidi_indexable(int*, ptr_at_edge_of_addr_space, sizeof(int)); + noop(); // break here: oob_ptr_plus_size_overflows is out-of-bounds + + //-------------------------------------------------------------------------- + // ptr > upper bound + //-------------------------------------------------------------------------- + char* __bidi_indexable oob_upper = data; // still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // Upper bound not aligned with type + //-------------------------------------------------------------------------- + _Static_assert(sizeof(data) < sizeof(int), "oob-ness requirement failed"); + // oob_upper_type_pun is out-of-bounds because reading the sizeof(int) bytes + // will read past the end of data. + int* __bidi_indexable oob_upper_type_pun = (int* __bidi_indexable) data; + noop(); // break here: oob_upper_type_pun is out-of-bounds + + //-------------------------------------------------------------------------- + // nullptr + //-------------------------------------------------------------------------- + int* __bidi_indexable oob_null = 0x0; + noop(); // break here: oob_null is out-of-bounds + + //-------------------------------------------------------------------------- + // Flexible array member + //-------------------------------------------------------------------------- + FAMS_t *__bidi_indexable fams = + alloc_fams_buffer(/*num_fams=*/2, /*num_elts_in_buffer=*/4); + FAMS_t *__bidi_indexable fams0 = fams; + noop(); // break here: fams0 in bounds + + FAMS_t *__bidi_indexable fams1 = get_next_fam_struct(fams0); + noop(); // break here: fams1 in bounds + + FAMS_t *__bidi_indexable fams2 = get_next_fam_struct(fams1); + noop(); // break here: fams2 is out-of-bounds + + free(fams); + return; +} diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_unknown_type_size.c b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_unknown_type_size.c new file mode 100644 index 0000000000000..9586e8e941278 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/bidi_check_unknown_type_size.c @@ -0,0 +1,41 @@ +#include +#include +#include + +static void noop() {} + +void bidi_check_unknown_type_size() { + char data[] = "fo"; + //-------------------------------------------------------------------------- + // ptr < lower bound + //-------------------------------------------------------------------------- + void *__bidi_indexable oob_ptr_lower = data; // still in-bounds + oob_ptr_lower -= 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // ptr + type size overflows + //-------------------------------------------------------------------------- + void *__unsafe_indexable ptr_at_edge_of_addr_space = + (void *__unsafe_indexable)UINTPTR_MAX; + void *__bidi_indexable oob_ptr_plus_size_overflows = + __unsafe_forge_bidi_indexable(void *, ptr_at_edge_of_addr_space, + sizeof(int)); + noop(); // break here: oob_ptr_plus_size_overflows is now out-of-bounds + + //-------------------------------------------------------------------------- + // ptr > upper bound + //-------------------------------------------------------------------------- + void *__bidi_indexable oob_upper = data; // still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // nullptr + //-------------------------------------------------------------------------- + void *__bidi_indexable oob_null = 0x0; + noop(); // break here: oob_null is out-of-bounds + return; +} diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_known_type_size.c b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_known_type_size.c new file mode 100644 index 0000000000000..f843c94122078 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_known_type_size.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +static void noop(){}; + +typedef struct FAMS { + int count; + int buffer[__counted_by(count)]; +} FAMS_t; + +static FAMS_t *__indexable get_next_fam_struct(FAMS_t *__indexable current) { + uintptr_t current_size = sizeof(*current) + (sizeof(int) * current->count); + uintptr_t next = ((uintptr_t)current) + current_size; + uintptr_t num_bytes = + ((uintptr_t)__ptr_upper_bound(current)) - ((uintptr_t)current); + uintptr_t remaining_bytes = 0; + if (num_bytes > current_size) + remaining_bytes = num_bytes - current_size; + return __unsafe_forge_bidi_indexable(FAMS_t *, next, remaining_bytes); +} + +static FAMS_t *__indexable alloc_fams_buffer(size_t num_fams, + size_t num_elts_in_buffer) { + const size_t buffer_size = sizeof(int) * num_elts_in_buffer; + FAMS_t *__indexable fams = malloc(num_fams * (sizeof(FAMS_t) + buffer_size)); + // Set the counts and zero init buffer + FAMS_t *__indexable current = fams; + for (size_t fam_num = 0; fam_num < num_fams; ++fam_num) { + current->count = num_elts_in_buffer; + // Don't zero-init due to rdar://102831737 + // memset(current->buffer, 0, buffer_size); + current = get_next_fam_struct(current); + } + return fams; +} + +void idx_check_known_type_size() { + char data[] = "fo"; + + //-------------------------------------------------------------------------- + // ptr + type size overflows + //-------------------------------------------------------------------------- + int *__unsafe_indexable ptr_at_edge_of_addr_space = + (int *__unsafe_indexable)UINTPTR_MAX; + int *__indexable oob_ptr_plus_size_overflows = __unsafe_forge_bidi_indexable( + int *, ptr_at_edge_of_addr_space, sizeof(int)); + noop(); // break here: oob_ptr_plus_size_overflows is out-of-bounds + + //-------------------------------------------------------------------------- + // ptr > upper bound + //-------------------------------------------------------------------------- + char *__indexable oob_upper = data; // still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // Upper bound not aligned with type + //-------------------------------------------------------------------------- + _Static_assert(sizeof(data) < sizeof(int), "oob-ness requirement failed"); + // oob_upper_type_pun is out-of-bounds because reading the sizeof(int) bytes + // will read past the end of data. + int *__indexable oob_upper_type_pun = (int *__indexable)data; + noop(); // break here: oob_upper_type_pun is out-of-bounds + + //-------------------------------------------------------------------------- + // nullptr + //-------------------------------------------------------------------------- + int *__indexable oob_null = 0x0; + noop(); // break here: oob_null is out-of-bounds + + //-------------------------------------------------------------------------- + // Flexible array member + //-------------------------------------------------------------------------- + FAMS_t *__indexable fams = + alloc_fams_buffer(/*num_fams=*/2, /*num_elts_in_buffer=*/4); + FAMS_t *__indexable fams0 = fams; + noop(); // break here: fams0 in bounds + + FAMS_t *__indexable fams1 = get_next_fam_struct(fams0); + noop(); // break here: fams1 in bounds + + FAMS_t *__indexable fams2 = get_next_fam_struct(fams1); + noop(); // break here: fams2 is out-of-bounds + + free(fams); + return; +} diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_unknown_type_size.c b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_unknown_type_size.c new file mode 100644 index 0000000000000..e743964912a20 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/idx_check_unknown_type_size.c @@ -0,0 +1,34 @@ +#include +#include +#include + +static void noop(){}; + +void idx_check_unknown_type_size() { + char data[] = "fo"; + + //-------------------------------------------------------------------------- + // ptr + type size overflows + //-------------------------------------------------------------------------- + void *__unsafe_indexable ptr_at_edge_of_addr_space = + (void *__unsafe_indexable)UINTPTR_MAX; + void *__indexable oob_ptr_plus_size_overflows = __unsafe_forge_bidi_indexable( + void *, ptr_at_edge_of_addr_space, sizeof(int)); + noop(); // break here: oob_ptr_plus_size_overflows is out-of-bounds + + //-------------------------------------------------------------------------- + // ptr > upper bound + //-------------------------------------------------------------------------- + void *__indexable oob_upper = data; // still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + oob_upper += 1; // break here: still in-bounds + noop(); // break here: Now out-of-bounds + + //-------------------------------------------------------------------------- + // nullptr + //-------------------------------------------------------------------------- + void *__indexable oob_null = 0x0; + noop(); // break here: oob_null is out-of-bounds + return; +} diff --git a/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/main.c b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/main.c new file mode 100644 index 0000000000000..3c3b7164fcd5b --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/out_of_bounds_pointer/main.c @@ -0,0 +1,12 @@ +void bidi_check_known_type_size(); +void bidi_check_unknown_type_size(); +void idx_check_known_type_size(); +void idx_check_unknown_type_size(); + +int main(void) { + bidi_check_known_type_size(); + bidi_check_unknown_type_size(); + idx_check_known_type_size(); + idx_check_unknown_type_size(); + return 0; +} diff --git a/lldb/test/API/lang/BoundsSafety/pointer_attributes/Makefile b/lldb/test/API/lang/BoundsSafety/pointer_attributes/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/pointer_attributes/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/pointer_attributes/TestBoundsSafetyPointerAttributes.py b/lldb/test/API/lang/BoundsSafety/pointer_attributes/TestBoundsSafetyPointerAttributes.py new file mode 100644 index 0000000000000..6f7d507d3ef07 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/pointer_attributes/TestBoundsSafetyPointerAttributes.py @@ -0,0 +1,62 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBoundsSafetyPointerAttributes(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) + + # Check if pointer types are displayed as expected. + self.expect_expr("biPtr", result_type="S *__bidi_indexable") + self.expect_expr("iPtr", result_type="S *__indexable") + self.expect_expr("sPtr", result_type="S *") + self.expect_expr("uiPtr", result_type="S *") + self.expect_expr("uPtr", result_type="S *__bidi_indexable") + + self.expect("expr biPtr", + substrs=['(S *__bidi_indexable)', + 'bounds:', + '..']) + self.expect("expr iPtr", + substrs=['(S *__indexable)', + 'upper bound:']) + self.expect("expr uPtr", + substrs=['(S *__bidi_indexable)', + 'bounds:', + '..']) + # Check if member access via attributed pointers works. + self.expect("expr biPtr->vptr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(void *)') + self.expect("expr iPtr->vptr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(void *)') + self.expect("expr sPtr->vptr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(void *)') + self.expect("expr uiPtr->vptr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(void *)') + self.expect("expr uPtr->vptr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(void *)') + self.expect_expr("biPtr->len", result_value='4') + self.expect_expr("iPtr->len", result_value='4') + self.expect_expr("sPtr->len", result_value='4') + self.expect_expr("uiPtr->len", result_value='4') + self.expect_expr("uPtr->len", result_value='4') + + self.expect("expr *biPtr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(S)') + self.expect("expr *iPtr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(S)') + self.expect("expr *sPtr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(S)') + self.expect("expr *uiPtr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(S)') + self.expect("expr *uPtr", VARIABLES_DISPLAYED_CORRECTLY, + startstr='(S)') diff --git a/lldb/test/API/lang/BoundsSafety/pointer_attributes/main.c b/lldb/test/API/lang/BoundsSafety/pointer_attributes/main.c new file mode 100644 index 0000000000000..4692a71812e51 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/pointer_attributes/main.c @@ -0,0 +1,20 @@ +#include + +typedef struct { + void *__sized_by(len) vptr; + int len; +} S; + +int main() { + char arr[] = {1, 2, 3, 4}; + S a = {arr, sizeof(arr)}; + S *__bidi_indexable biPtr = &a; + S *__indexable iPtr = &a; + S *__single sPtr = &a; + S *__unsafe_indexable uiPtr = &a; + S *uPtr = &a; + + biPtr = &a; + + return 0; // break here +} diff --git a/lldb/test/API/lang/BoundsSafety/ptr_attributes/Makefile b/lldb/test/API/lang/BoundsSafety/ptr_attributes/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/ptr_attributes/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/ptr_attributes/TestAttributeTypes.py b/lldb/test/API/lang/BoundsSafety/ptr_attributes/TestAttributeTypes.py new file mode 100644 index 0000000000000..222e3e315fe7e --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/ptr_attributes/TestAttributeTypes.py @@ -0,0 +1,41 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicTypes(TestBase): + + + def test(self): + self.build() + + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + ptr_regex = '0x[0-9a-f]+' + + counted_by_pattern = f'\(int \*\) .* = \(ptr: {ptr_regex} counted_by: num_elements - 1\)' + self.expect("frame var ptr_counted_by", patterns = [counted_by_pattern]) + self.expect("expr ptr_counted_by", patterns = [counted_by_pattern]) + + sized_by_pattern = f'\(int \*\) .* \(ptr: {ptr_regex} sized_by: num_elements \* 4\)' + self.expect("frame var ptr_sized_by", patterns = [sized_by_pattern]) + self.expect("expr ptr_sized_by", patterns = [sized_by_pattern]) + + counted_by_or_null_pattern = f'\(int \*\) .* = \(ptr: {ptr_regex} counted_by_or_null: num_elements - 5\)' + self.expect("frame var ptr_counted_by_or_null", patterns = [counted_by_or_null_pattern]) + self.expect("expr ptr_counted_by_or_null", patterns = [counted_by_or_null_pattern]) + + sized_by_or_null_pattern = f'\(int \*\) .* \(ptr: {ptr_regex} sized_by_or_null: num_elements \* 2\)' + self.expect("frame var ptr_sized_by_or_null", patterns = [sized_by_or_null_pattern]) + self.expect("expr ptr_sized_by_or_null", patterns = [sized_by_or_null_pattern]) + + ended_by_pattern = f'\(int \*\) .* \(ptr: {ptr_regex} end_expr: end\)' + self.expect("frame var ptr_ended_by", patterns = [ended_by_pattern]) + self.expect("expr ptr_ended_by", patterns = [ended_by_pattern]) + + # Using "end" inside an attribute like `ended_by(end)` makes it become + # a pointer with a "start expr". + end_pattern = f'\(int \*\) .* \(ptr: {ptr_regex} start_expr: ptr_ended_by\)' + self.expect("frame var end", patterns = [end_pattern]) + self.expect("expr end", patterns = [end_pattern]) + diff --git a/lldb/test/API/lang/BoundsSafety/ptr_attributes/main.c b/lldb/test/API/lang/BoundsSafety/ptr_attributes/main.c new file mode 100644 index 0000000000000..6b17fa398163f --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/ptr_attributes/main.c @@ -0,0 +1,24 @@ +#include + +int test_attrs(int *__counted_by(num_elements - 1) ptr_counted_by, + int *__sized_by(num_elements * 4) ptr_sized_by, + int *__counted_by_or_null(num_elements - 5) ptr_counted_by_or_null, + int *__sized_by_or_null(num_elements * 2) ptr_sized_by_or_null, int *end, + int *__ended_by(end) ptr_ended_by, int num_elements) { + return 0; // break here + // We expect to see this kind of output for a frame var: + // (__bounds_safety::counted_by::num_elements - 1) ptr_counted_by = (int * ptr: 0x00016fdff0e0 counted_by: num_elements - 1) + // (__bounds_safety::sized_by::num_elements * 4) ptr_sized_by = (int * ptr: 0x00016fdff0e0 sized_by: num_elements * 4) + // (__bounds_safety::counted_by_or_null::num_elements - 5) ptr_counted_by_or_null = (int * ptr: 0x00016fdff0e0 counted_by_or_null: num_elements - 5) + // (__bounds_safety::sized_by_or_null::num_elements * 2) ptr_sized_by_or_null = (int * ptr: 0x00016fdff0e0 sized_by_or_null: num_elements * 2) + // (__bounds_safety::dynamic_range::ptr_ended_by::) end = (int * ptr: 0x00016fdff0f4 start_expr: ptr_ended_by) + // (__bounds_safety::dynamic_range::::end) ptr_ended_by = (int * ptr: 0x00016fdff0e0 end_expr: end) +} + +int main() { + int array[6] = {1, 2, 3, 4, 5, 6}; + test_attrs(array /*counted_by*/, array /*sized_by*/, + array /*counted_by_or_null*/, array /*sized_by_or_null*/, + array + 5 /*end*/, array /*ended by*/, 6); + return 0; // break here +} diff --git a/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/Makefile b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/TestSizedOrNullPointer.py b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/TestSizedOrNullPointer.py new file mode 100644 index 0000000000000..9e9eb82b27800 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/TestSizedOrNullPointer.py @@ -0,0 +1,29 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestSizedPointer(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + + def test(self): + self.build() + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + self.expect_expr("strArg", result_type="char *") + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect_expr("buf", result_type="int *") + # Debugging will still work + self.expect_expr("buf[len]", result_type="int") + self.expect_expr("buf[len-1]", result_value='5') + + self.expect("expr str", VARIABLES_DISPLAYED_CORRECTLY, + substrs=["Hello!"]) + self.expect_expr("ch", result_value="'!'") + + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.expect_expr("strArg == NULL", result_type="bool", result_value="true") diff --git a/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/main.c b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/main.c new file mode 100644 index 0000000000000..4abb969dc4603 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_or_null_pointer/main.c @@ -0,0 +1,26 @@ +#include + +typedef struct { + int len; + int *__counted_by_or_null(len) buf; +} S; + +char foo(char *__sized_by_or_null(l) strArg, unsigned int l) { + return strArg[l-1]; // break here +} + +int main() { + int arr[] = {1, 2, 3, 4, 5}; + int len; + int *__counted_by_or_null(len) buf; + len = sizeof(arr) / sizeof(int); + buf = arr; + + S s = {len, buf}; + + char *str = "Hello!"; + char ch = foo(str, 6); + // break here + char ch2 = foo((void*)0, 6); + return 0; +} diff --git a/lldb/test/API/lang/BoundsSafety/sized_pointer/Makefile b/lldb/test/API/lang/BoundsSafety/sized_pointer/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_pointer/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/sized_pointer/TestSizedPointer.py b/lldb/test/API/lang/BoundsSafety/sized_pointer/TestSizedPointer.py new file mode 100644 index 0000000000000..7f62b810e06c7 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_pointer/TestSizedPointer.py @@ -0,0 +1,26 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestSizedPointer(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + + def test(self): + self.build() + (_, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + self.expect_expr("strArg", result_type="char *") + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect_expr("buf", result_type="int *") + # Debugging will still work + self.expect_expr("buf[len]", result_type="int") + self.expect_expr("buf[len-1]", result_value='5') + + self.expect("expr str", VARIABLES_DISPLAYED_CORRECTLY, + substrs=["Hello!"]) + self.expect_expr("ch", result_value="'!'") diff --git a/lldb/test/API/lang/BoundsSafety/sized_pointer/main.c b/lldb/test/API/lang/BoundsSafety/sized_pointer/main.c new file mode 100644 index 0000000000000..7e184ea4a6a9f --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/sized_pointer/main.c @@ -0,0 +1,24 @@ +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +char foo(char *__sized_by(l) strArg, unsigned int l) { + return strArg[l-1]; // break here +} + +int main() { + int arr[] = {1, 2, 3, 4, 5}; + int len; + int *__counted_by(len) buf; + len = sizeof(arr) / sizeof(int); + buf = arr; + + S s = {len, buf}; + + char *str = "Hello!"; + char ch = foo(str, 6); + return 0; // break here +} diff --git a/lldb/test/API/lang/BoundsSafety/wide_pointer_args/Makefile b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/Makefile new file mode 100644 index 0000000000000..c7b7ea0b868fa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -Xclang -fbounds-safety + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/wide_pointer_args/TestWidePointerArgs.py b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/TestWidePointerArgs.py new file mode 100644 index 0000000000000..14bce2554f098 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/TestWidePointerArgs.py @@ -0,0 +1,77 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestWidePointerArgs(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs=[' resolved, hit count = 1']) + + self.expect( + "frame variable --show-types callback", + VARIABLES_DISPLAYED_CORRECTLY, + startstr='(int (*)(int *__bidi_indexable)) callback =') + + def test_call(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + self.expect_expr("foo(&arr[1])", result_type="int", result_value="2") + self.expect_expr("foo(ptrImplicitBidiIndex)", result_type="int", result_value="1") + self.expect_expr("foo(ptrBidiIndex)", result_type="int", result_value="2") + self.expect_expr("foo(ptrIndex)", result_type="int", result_value="3") + self.expect_expr("foo(ptrSingle)", result_type="int", result_value="4") + self.expect_expr("foo(ptrUnsafe)", result_type="int", result_value="5") + self.expect_expr("(void*)ptrImplicitBidiIndex", result_type="void *") + self.expect_expr("(void*)ptrBidiIndex", result_type="void *") + self.expect_expr("(void*)ptrIndex", result_type="void *") + self.expect_expr("(void*)ptrSingle", result_type="void *") + + self.expect_expr("ptrImplicitBidiIndex ? ptrImplicitBidiIndex : 0", result_type="int *__bidi_indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrBidiIndex : 0", result_type="int *__bidi_indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrIndex : 0", result_type="int *__indexable") + self.expect_expr("!ptrImplicitBidiIndex ? ptrImplicitBidiIndex : arr", result_type="int *") + self.expect_expr("ptrImplicitBidiIndex ? arr : ptrBidiIndex", result_type="int *") + self.expect_expr("ptrImplicitBidiIndex ? ptrIndex : &arr[0]", result_type="int *") + self.expect_expr("ptrImplicitBidiIndex ? ptrBidiIndex : ptrSingle", result_type="int *__bidi_indexable") + self.expect_expr("!ptrImplicitBidiIndex ? ptrBidiIndex : ptrUnsafe", result_type="int *__bidi_indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrImplicitBidiIndex : ptrIndex", result_type="int *__bidi_indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrIndex : ptrBidiIndex", result_type="int *__indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrIndex : ptrIndex", result_type="int *__indexable") + self.expect_expr("ptrImplicitBidiIndex ? ptrBidiIndex : ptrIndex", result_type="int *__bidi_indexable") + + self.expect_expr("ptrIndex ? ptrImplicitBidiIndex : 0", result_type="int *__bidi_indexable") + self.expect_expr("ptrIndex ? ptrBidiIndex : 0", result_type="int *__bidi_indexable") + self.expect_expr("ptrBidiIndex ? ptrIndex : 0", result_type="int *__indexable") + self.expect_expr("ptrBidiIndex ? ptrImplicitBidiIndex : arr", result_type="int *") + self.expect_expr("ptrIndex ? &arr[0] : ptrBidiIndex", result_type="int *") + self.expect_expr("ptrBidiIndex ? ptrIndex : ptrSingle", result_type="int *__indexable") + self.expect_expr("ptrIndex ? ptrBidiIndex : arr", result_type="int *") + self.expect_expr("ptrBidiIndex ? ptrImplicitBidiIndex : ptrIndex", result_type="int *__bidi_indexable") + self.expect_expr("ptrBidiIndex ? ptrIndex : ptrBidiIndex", result_type="int *__indexable") + self.expect_expr("ptrIndex ? ptrIndex : ptrIndex") + self.expect_expr("!ptrIndex ? ptrBidiIndex : ptrIndex", result_type="int *__bidi_indexable") + + self.expect_expr("ptrBidiIndex ?: 0", result_type="int *__bidi_indexable") + self.expect_expr("ptrIndex ?: 0", result_type="int *__indexable") + + self.expect_expr("callback(&arr[0])", result_type="int", result_value="1") + self.expect_expr("callback_s(ptrImplicitBidiIndex)", result_type="int", result_value="1") + self.expect_expr("callback_s(ptrBidiIndex)", result_type="int", result_value="2") + self.expect_expr("callback_s(ptrIndex)", result_type="int", result_value="3") + self.expect_expr("callback_s(ptrSingle)", result_type="int", result_value="4") + self.expect_expr("callback_s(ptrUnsafe)", result_type="int", result_value="5") + self.expect_expr("callback_s(arr)", result_type="int", result_value="1") + # We still have a correctness issue here that assignments to a wide pointer only changes the pointer value. + self.expect_expr("ptrImplicitBidiIndex = baz(ptrBidiIndex)", result_type="int *__bidi_indexable") + self.expect_expr("ptrBidiIndex = baz(ptrSingle)", result_type="int *__bidi_indexable") + self.expect_expr("ptrIndex = ptrImplicitBidiIndex", result_type="int *__indexable") diff --git a/lldb/test/API/lang/BoundsSafety/wide_pointer_args/main.c b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/main.c new file mode 100644 index 0000000000000..2b0eb7474890a --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/wide_pointer_args/main.c @@ -0,0 +1,25 @@ +#include + +int foo(int *__bidi_indexable biArg) { + return *biArg; +} + +int bar(int *sArg) { + return *sArg; +} + +int *__bidi_indexable baz(int *__bidi_indexable biArg) { + return biArg; +} + +int main() { + int arr[] = {1, 2, 3, 4, 5}; + int *ptrImplicitBidiIndex = arr; + int *__bidi_indexable ptrBidiIndex = arr + 1; + int *__indexable ptrIndex = arr + 2; + int *__single ptrSingle = arr + 3; + int *__unsafe_indexable ptrUnsafe = arr + 4; + int (*callback)(int *__bidi_indexable) = foo; + int (*callback_s)(int *) = bar; + callback(arr); // break here +} diff --git a/lldb/test/API/lang/c/conflicting-symbol/Makefile b/lldb/test/API/lang/c/conflicting-symbol/Makefile index 81594a1265da2..1331c4e1ebfaf 100644 --- a/lldb/test/API/lang/c/conflicting-symbol/Makefile +++ b/lldb/test/API/lang/c/conflicting-symbol/Makefile @@ -7,4 +7,4 @@ include Makefile.rules a.out: lib_One lib_Two lib_%: - $(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/$*.mk + "$(MAKE)" VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/$*.mk diff --git a/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py index 9e484e0132c83..730537da2fccf 100644 --- a/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py +++ b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py @@ -5,9 +5,6 @@ class TestCaseClassTemplateNonTypeParameterPack(TestBase): - @expectedFailureAll( - oslist=["windows"], archs=["i[3-6]86", "x86_64"] - ) # Fails to read memory from target. @no_debug_info_test def test(self): self.build() diff --git a/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py index 102c00dc603df..1ed643e6dd85c 100644 --- a/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py +++ b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py @@ -5,9 +5,6 @@ class TestCaseClassTemplateTypeParameterPack(TestBase): - @expectedFailureAll( - oslist=["windows"], archs=["i[3-6]86", "x86_64"] - ) # Fails to read memory from target. @no_debug_info_test def test(self): self.build() diff --git a/lldb/test/API/lang/cpp/incomplete-types/Makefile b/lldb/test/API/lang/cpp/incomplete-types/Makefile index f42ac2e81cc76..0cf3f6a31caa2 100644 --- a/lldb/test/API/lang/cpp/incomplete-types/Makefile +++ b/lldb/test/API/lang/cpp/incomplete-types/Makefile @@ -16,7 +16,7 @@ main.o: CFLAGS_EXTRAS = -flimit-debug-info limit: a.o main.o mkdir -p build_limit - $(MAKE) -C $(BUILDDIR)/build_limit -f $(MAKEFILE_RULES) \ + "$(MAKE)" -C $(BUILDDIR)/build_limit -f $(MAKEFILE_RULES) \ EXE=../limit CXX_SOURCES="length.cpp ../a.o ../main.o" \ CFLAGS_EXTRAS=-flimit-debug-info NO_LIMIT_DEBUG_INFO_FLAGS="" diff --git a/lldb/test/API/lang/cpp/namespace_definitions/Makefile b/lldb/test/API/lang/cpp/namespace_definitions/Makefile index fc9165f67f428..b17d70fc92871 100644 --- a/lldb/test/API/lang/cpp/namespace_definitions/Makefile +++ b/lldb/test/API/lang/cpp/namespace_definitions/Makefile @@ -6,10 +6,10 @@ a.out: liba libb include Makefile.rules liba: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=a DYLIB_CXX_SOURCES=a.cpp libb: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=b DYLIB_CXX_SOURCES=b.cpp diff --git a/lldb/test/API/lang/objc/conflicting-definition/Makefile b/lldb/test/API/lang/objc/conflicting-definition/Makefile index 00a0769a086f0..cba79c94d46ba 100644 --- a/lldb/test/API/lang/objc/conflicting-definition/Makefile +++ b/lldb/test/API/lang/objc/conflicting-definition/Makefile @@ -9,14 +9,14 @@ include Makefile.rules libTest.dylib: Test/Test.m mkdir -p Test - $(MAKE) MAKE_DSYM=YES -f $(MAKEFILE_RULES) \ + "$(MAKE)" MAKE_DSYM=YES -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=Test DYLIB_OBJC_SOURCES=Test/Test.m \ LD_EXTRAS="-lobjc -framework Foundation" \ CFLAGS_EXTRAS=-I$(SRCDIR) libTestExt.dylib: TestExt/TestExt.m mkdir -p TestExt - $(MAKE) MAKE_DSYM=YES -f $(MAKEFILE_RULES) \ + "$(MAKE)" MAKE_DSYM=YES -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=TestExt DYLIB_OBJC_SOURCES=TestExt/TestExt.m \ LD_EXTRAS="-lobjc -framework Foundation -lTest -L." \ CFLAGS_EXTRAS=-I$(SRCDIR) diff --git a/lldb/test/API/lang/objc/modules-hash-mismatch/Makefile b/lldb/test/API/lang/objc/modules-hash-mismatch/Makefile index 59bf009f68677..57da670b69ab3 100644 --- a/lldb/test/API/lang/objc/modules-hash-mismatch/Makefile +++ b/lldb/test/API/lang/objc/modules-hash-mismatch/Makefile @@ -5,7 +5,7 @@ USE_PRIVATE_MODULE_CACHE = YES .PHONY: update-module all: $(EXE) - $(MAKE) -f $(SRCDIR)/Makefile update-module + "$(MAKE)" -f $(SRCDIR)/Makefile update-module include Makefile.rules diff --git a/lldb/test/API/lang/swift/async/tasks/Makefile b/lldb/test/API/lang/swift/async/tasks/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/async/tasks/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -parse-as-library +include Makefile.rules diff --git a/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskBacktrace.py b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskBacktrace.py new file mode 100644 index 0000000000000..694c800ed55b6 --- /dev/null +++ b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskBacktrace.py @@ -0,0 +1,21 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import TestBase +import lldbsuite.test.lldbutil as lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.expect( + "language swift task backtrace task", + substrs=[ + ".sleep(", + "`second() at main.swift:6", + "`first() at main.swift:2", + "`closure #1 in static Main.main() at main.swift:12", + ], + ) diff --git a/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py new file mode 100644 index 0000000000000..61d4f38d0b056 --- /dev/null +++ b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py @@ -0,0 +1,55 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import TestBase +import lldbsuite.test.lldbutil as lldbutil + + +class TestCase(TestBase): + + def test_backtrace_selected_task(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.runCmd("language swift task select task") + self.expect( + "thread backtrace", + substrs=[ + ".sleep(", + "`second() at main.swift:6:", + "`first() at main.swift:2:", + "`closure #1 in static Main.main() at main.swift:12:", + ], + ) + + def test_navigate_selected_task_stack(self): + self.build() + _, process, _, _ = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.runCmd("language swift task select task") + + thread = process.selected_thread + self.assertEqual(thread.id, 2) + self.assertEqual(thread.idx, 0xFFFFFFFF) + self.assertIn( + "libswift_Concurrency.", thread.GetSelectedFrame().module.file.basename + ) + + frame_idx = -1 + for frame in thread: + if "`second()" in str(frame): + frame_idx = frame.idx + self.assertNotEqual(frame_idx, -1) + + self.expect(f"frame select {frame_idx}", substrs=[f"frame #{frame_idx}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".second()", frame.function.name) + + self.expect("up", substrs=[f"frame #{frame_idx + 1}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".first()", frame.function.name) + + self.expect("up", substrs=[f"frame #{frame_idx + 2}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".Main.main()", frame.function.name) diff --git a/lldb/test/API/lang/swift/async/tasks/main.swift b/lldb/test/API/lang/swift/async/tasks/main.swift new file mode 100644 index 0000000000000..11dd1a1a30860 --- /dev/null +++ b/lldb/test/API/lang/swift/async/tasks/main.swift @@ -0,0 +1,17 @@ +func first() async { + await second() +} + +func second() async { + try? await Task.sleep(for: .seconds(10)) +} + +@main struct Main { + static func main() async { + let task = Task { + await first() + } + try? await Task.sleep(for: .seconds(0.01)) + print("break here") + } +} diff --git a/lldb/test/API/lang/swift/clashing_abi_name/Makefile b/lldb/test/API/lang/swift/clashing_abi_name/Makefile index 2237cdac0c039..553b52a209b66 100644 --- a/lldb/test/API/lang/swift/clashing_abi_name/Makefile +++ b/lldb/test/API/lang/swift/clashing_abi_name/Makefile @@ -7,11 +7,11 @@ all: libLibrary.dylib a.out include Makefile.rules libLibrary.dylib: Library.swift - $(MAKE) MAKE_DSYM=YES CC=$(CC) SWIFTC=$(SWIFTC) \ + "$(MAKE)" MAKE_DSYM=YES CC=$(CC) SWIFTC=$(SWIFTC) \ ARCH=$(ARCH) DSYMUTIL=$(DSYMUTIL) \ BASENAME=Library \ SWIFTFLAGS_EXTRAS="-I$(BUILDDIR) -enable-library-evolution -emit-library -emit-module -parse-as-library -module-abi-name a" \ VPATH=$(SRCDIR) -I $(SRCDIR) -f $(SRCDIR)/dylib.mk all clean:: - $(MAKE) BASENAME=Library VPATH=$(SRCDIR) -I $(SRCDIR) -f $(SRCDIR)/dylib.mk clean + "$(MAKE)" BASENAME=Library VPATH=$(SRCDIR) -I $(SRCDIR) -f $(SRCDIR)/dylib.mk clean diff --git a/lldb/test/API/lang/swift/expression/error_missing_type/Makefile b/lldb/test/API/lang/swift/expression/error_missing_type/Makefile index 8272b2ffbc43a..1879c567ae9aa 100644 --- a/lldb/test/API/lang/swift/expression/error_missing_type/Makefile +++ b/lldb/test/API/lang/swift/expression/error_missing_type/Makefile @@ -4,7 +4,7 @@ LD_EXTRAS = -lLibrary -L$(BUILDDIR) all: libLibrary.dylib a.out lib%.dylib: %.swift - $(MAKE) MAKE_DSYM=NO DYLIB_ONLY=YES \ + "$(MAKE)" MAKE_DSYM=NO DYLIB_ONLY=YES \ DYLIB_HIDE_SWIFTMODULE=YES \ DYLIB_NAME=$(shell basename $< .swift) \ DYLIB_SWIFT_SOURCES=$(shell basename $<) \ diff --git a/lldb/test/API/lang/swift/import_spi/Makefile b/lldb/test/API/lang/swift/import_spi/Makefile index 7bb7fa25c1aeb..325be6e6885c6 100644 --- a/lldb/test/API/lang/swift/import_spi/Makefile +++ b/lldb/test/API/lang/swift/import_spi/Makefile @@ -8,7 +8,7 @@ all: A.swiftmodule $(EXE) include Makefile.rules A.swiftmodule: $(SRCDIR)/A.swift B.swiftmodule - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES \ DYLIB_NAME=A \ DYLIB_SWIFT_SOURCES=A.swift \ @@ -17,7 +17,7 @@ A.swiftmodule: $(SRCDIR)/A.swift B.swiftmodule LD_EXTRAS="-L$(BUILDDIR) -lB" B.swiftmodule: $(SRCDIR)/B.swift - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES \ DYLIB_NAME=B \ DYLIB_SWIFT_SOURCES=B.swift \ diff --git a/lldb/test/API/lit.cfg.py b/lldb/test/API/lit.cfg.py index ee1e91597440a..d825ad330ff4f 100644 --- a/lldb/test/API/lit.cfg.py +++ b/lldb/test/API/lit.cfg.py @@ -278,6 +278,9 @@ def delete_module_cache(path): if is_configured("dsymutil"): dotest_cmd += ["--dsymutil", config.dsymutil] +if is_configured("make"): + dotest_cmd += ["--make", config.make] + if is_configured("llvm_tools_dir"): dotest_cmd += ["--llvm-tools-dir", config.llvm_tools_dir] diff --git a/lldb/test/API/lit.site.cfg.py.in b/lldb/test/API/lit.site.cfg.py.in index 4184436c63ba7..420e0aa4751b1 100644 --- a/lldb/test/API/lit.site.cfg.py.in +++ b/lldb/test/API/lit.site.cfg.py.in @@ -33,6 +33,7 @@ config.test_arch = '@LLDB_TEST_ARCH@' config.test_compiler = lit_config.substitute('@LLDB_TEST_COMPILER@') config.test_swift_compiler = lit_config.substitute('@LLDB_SWIFTC@') config.dsymutil = lit_config.substitute('@LLDB_TEST_DSYMUTIL@') +config.make = lit_config.substitute('@LLDB_TEST_MAKE@') config.has_libcxx = @LLDB_HAS_LIBCXX@ config.libcxx_libs_dir = "@LIBCXX_LIBRARY_DIR@" config.libcxx_include_dir = "@LIBCXX_GENERATED_INCLUDE_DIR@" diff --git a/lldb/test/API/macosx/delay-init-dependency/Makefile b/lldb/test/API/macosx/delay-init-dependency/Makefile index 246ea0f34e1a1..7421c68b79baa 100644 --- a/lldb/test/API/macosx/delay-init-dependency/Makefile +++ b/lldb/test/API/macosx/delay-init-dependency/Makefile @@ -7,5 +7,5 @@ all: build-libfoo a.out include Makefile.rules build-libfoo: foo.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_C_SOURCES=foo.c DYLIB_NAME=foo DYLIB_ONLY=YES diff --git a/lldb/test/API/macosx/expedited-thread-pcs/Makefile b/lldb/test/API/macosx/expedited-thread-pcs/Makefile index 7799f06e77097..73a969831e673 100644 --- a/lldb/test/API/macosx/expedited-thread-pcs/Makefile +++ b/lldb/test/API/macosx/expedited-thread-pcs/Makefile @@ -6,6 +6,6 @@ all: build-libfoo a.out include Makefile.rules build-libfoo: foo.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_C_SOURCES=foo.c DYLIB_NAME=foo DYLIB_ONLY=YES diff --git a/lldb/test/API/macosx/indirect_symbol/Makefile b/lldb/test/API/macosx/indirect_symbol/Makefile index 9069302b39c4f..dee3e184fe19b 100644 --- a/lldb/test/API/macosx/indirect_symbol/Makefile +++ b/lldb/test/API/macosx/indirect_symbol/Makefile @@ -7,11 +7,11 @@ all: build-libindirect build-libreepxoprt a.out include Makefile.rules build-libindirect: indirect.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_C_SOURCES=indirect.c DYLIB_NAME=indirect DYLIB_ONLY=YES \ LD_EXTRAS="-Wl,-image_base,0x200000000" build-libreepxoprt: reexport.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_C_SOURCES=reexport.c DYLIB_NAME=reexport DYLIB_ONLY=YES \ LD_EXTRAS="-L. -lindirect -Wl,-alias_list,$(SRCDIR)/alias.list" diff --git a/lldb/test/API/macosx/lc-note/kern-ver-str/Makefile b/lldb/test/API/macosx/lc-note/kern-ver-str/Makefile index 05d9552a80209..01b4acfdcfd2a 100644 --- a/lldb/test/API/macosx/lc-note/kern-ver-str/Makefile +++ b/lldb/test/API/macosx/lc-note/kern-ver-str/Makefile @@ -5,7 +5,7 @@ C_SOURCES := main.c all: a.out create-empty-corefile create-empty-corefile: - $(MAKE) -f $(MAKEFILE_RULES) EXE=create-empty-corefile \ + "$(MAKE)" -f $(MAKEFILE_RULES) EXE=create-empty-corefile \ CXX=$(CC) CXX_SOURCES=create-empty-corefile.cpp include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile index 8e561f17383fe..229235cda999f 100644 --- a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile @@ -10,11 +10,11 @@ create-empty-corefile: CXX_SOURCES=create-multibin-corefile.cpp libone.dylib: one.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=one DYLIB_C_SOURCES=one.c libtwo.dylib: two.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=two DYLIB_C_SOURCES=two.c include Makefile.rules diff --git a/lldb/test/API/macosx/macCatalystAppMacOSFramework/Makefile b/lldb/test/API/macosx/macCatalystAppMacOSFramework/Makefile index bb8df1869ef98..4d2ba1661ea30 100644 --- a/lldb/test/API/macosx/macCatalystAppMacOSFramework/Makefile +++ b/lldb/test/API/macosx/macCatalystAppMacOSFramework/Makefile @@ -9,7 +9,7 @@ override CC=xcrun clang all: libfoo.dylib a.out libfoo.dylib: foo.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_NAME=foo DYLIB_C_SOURCES=foo.c include Makefile.rules diff --git a/lldb/test/API/macosx/skinny-corefile/Makefile b/lldb/test/API/macosx/skinny-corefile/Makefile index efe37f3d2b8b2..fce43a36c33ac 100644 --- a/lldb/test/API/macosx/skinny-corefile/Makefile +++ b/lldb/test/API/macosx/skinny-corefile/Makefile @@ -6,10 +6,10 @@ include Makefile.rules a.out: libto-be-removed libpresent libto-be-removed: libpresent - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=to-be-removed.c DYLIB_NAME=to-be-removed \ LD_EXTRAS="-L. -lpresent" libpresent: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=present.c DYLIB_NAME=present diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/Makefile b/lldb/test/API/tools/lldb-dap/breakpoint/Makefile index 30a6400184933..7634f513e8523 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/Makefile +++ b/lldb/test/API/tools/lldb-dap/breakpoint/Makefile @@ -15,5 +15,5 @@ main-copy.cpp: main.cpp # The following shared library will be used to test breakpoints under dynamic loading libother: other-copy.c - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_ONLY=YES DYLIB_C_SOURCES=other-copy.c DYLIB_NAME=other diff --git a/lldb/test/API/tools/lldb-server/libraries-svr4/Makefile b/lldb/test/API/tools/lldb-server/libraries-svr4/Makefile index 5b5c1dcef783a..f13b1ac15928a 100644 --- a/lldb/test/API/tools/lldb-server/libraries-svr4/Makefile +++ b/lldb/test/API/tools/lldb-server/libraries-svr4/Makefile @@ -9,11 +9,11 @@ a.out: svr4lib_a svr4lib_b_quote include Makefile.rules svr4lib_a: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_NAME=svr4lib_a DYLIB_CXX_SOURCES=svr4lib_a.cpp \ DYLIB_ONLY=YES svr4lib_b_quote: - $(MAKE) -f $(MAKEFILE_RULES) \ + "$(MAKE)" -f $(MAKEFILE_RULES) \ DYLIB_NAME=svr4lib_b\\\" DYLIB_CXX_SOURCES=svr4lib_b_quote.cpp \ DYLIB_ONLY=YES diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 25dc559ed926f..aef43dda67660 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -3,7 +3,7 @@ # Lit requires a Python3 interpreter, let's be careful and fail early if it's # not present. if (NOT DEFINED Python3_EXECUTABLE) - message(FATAL_ERROR + message(SEND_ERROR "LLDB test suite requires a Python3 interpreter but none " "was found. Please install Python3 or disable tests with " "`LLDB_INCLUDE_TESTS=OFF`.") @@ -12,7 +12,7 @@ endif() if(LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS) message(STATUS "Enforcing strict test requirements for LLDB") # Lit uses psutil to do per-test timeouts. - set(useful_python_modules psutil) + set(useful_python_modules psutil packaging) if(NOT WIN32) # We no longer vendor pexpect and it is not used on Windows. @@ -22,7 +22,7 @@ if(LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS) foreach(module ${useful_python_modules}) lldb_find_python_module(${module}) if (NOT PY_${module}_FOUND) - message(FATAL_ERROR + message(SEND_ERROR "Python module '${module}' not found. Please install it via pip or via " "your operating system's package manager. Alternatively, disable " "strict testing requirements with " @@ -66,10 +66,10 @@ if (LLDB_TEST_OBJC_GNUSTEP) find_package(GNUstepObjC) if (NOT GNUstepObjC_FOUND) if (LLDB_TEST_OBJC_GNUSTEP_DIR) - message(FATAL_ERROR "Failed to find GNUstep libobjc2 in ${LLDB_TEST_OBJC_GNUSTEP_DIR}. " + message(SEND_ERROR "Failed to find GNUstep libobjc2 in ${LLDB_TEST_OBJC_GNUSTEP_DIR}. " "Please check LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") else() - message(FATAL_ERROR "Failed to find GNUstep libobjc2. " + message(SEND_ERROR "Failed to find GNUstep libobjc2. " "Please set LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") endif() endif() @@ -185,7 +185,7 @@ if(TARGET clang) set(LIBCXX_LIBRARY_DIR "${LLDB_TEST_LIBCXX_ROOT_DIR}/lib${LIBCXX_LIBDIR_SUFFIX}") set(LIBCXX_GENERATED_INCLUDE_DIR "${LLDB_TEST_LIBCXX_ROOT_DIR}/include/c++/v1") else() - message(FATAL_ERROR + message(SEND_ERROR "Couldn't find libcxx build in '${LLDB_TEST_LIBCXX_ROOT_DIR}'. To run the " "test-suite for a standalone LLDB build please build libcxx and point " "LLDB_TEST_LIBCXX_ROOT_DIR to it.") @@ -194,7 +194,7 @@ if(TARGET clang) # We require libcxx for the test suite, so if we aren't building it, # provide a helpful error about how to resolve the situation. if(NOT LLDB_HAS_LIBCXX) - message(FATAL_ERROR + message(SEND_ERROR "LLDB test suite requires libc++, but it is currently disabled. " "Please add `libcxx` to `LLVM_ENABLE_RUNTIMES` or disable tests via " "`LLDB_INCLUDE_TESTS=OFF`.") diff --git a/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyTrap.c b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyTrap.c new file mode 100644 index 0000000000000..77f9241c4d3d1 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyTrap.c @@ -0,0 +1,10 @@ +int bad_read(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +int main() { + bad_read(10); + + return 0; +} diff --git a/lldb/test/Shell/BoundsSafety/boundssafetytrap.test b/lldb/test/Shell/BoundsSafety/boundssafetytrap.test new file mode 100644 index 0000000000000..46aa225247f2f --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafetytrap.test @@ -0,0 +1,10 @@ +# UNSUPPORTED: system-windows +# RUN: %clang_host -Xclang -fbounds-safety -g -O0 %S/Inputs/boundsSafetyTrap.c -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s +run +# CHECK: thread #{{.*}}stop reason = Bounds check failed: Dereferencing above bounds +frame info +# CHECK: frame #{{.*}}`bad_read(index=10) at boundsSafetyTrap.c +frame recognizer info 0 +# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer +q diff --git a/lldb/test/Shell/BoundsSafety/lit.local.cfg b/lldb/test/Shell/BoundsSafety/lit.local.cfg new file mode 100644 index 0000000000000..945a5615c4219 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/lit.local.cfg @@ -0,0 +1,2 @@ +if 'python' not in config.available_features: + config.unsupported = True diff --git a/lldb/test/Shell/Commands/command-disassemble-mixed.c b/lldb/test/Shell/Commands/command-disassemble-mixed.c index 1e530095c5c56..4af85c0a4c020 100644 --- a/lldb/test/Shell/Commands/command-disassemble-mixed.c +++ b/lldb/test/Shell/Commands/command-disassemble-mixed.c @@ -1,6 +1,6 @@ // invalid mixed disassembly line -// XFAIL: system-windows +// XFAIL: target-windows // RUN: %clang_host -g %s -o %t // RUN: %lldb %t -o "dis -m -n main" -o "exit" | FileCheck %s diff --git a/lldb/test/Shell/Commands/command-expr-diagnostics.test b/lldb/test/Shell/Commands/command-expr-diagnostics.test index b242dba1980f0..3c827fb4516ec 100644 --- a/lldb/test/Shell/Commands/command-expr-diagnostics.test +++ b/lldb/test/Shell/Commands/command-expr-diagnostics.test @@ -1,3 +1,4 @@ +# XFAIL: target-windows # RUN: echo quit | %lldb -o "expression a+b" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK1 # (lldb) expression a+b diff --git a/lldb/test/Shell/Commands/command-target-create-resolve-exe.test b/lldb/test/Shell/Commands/command-target-create-resolve-exe.test index 0ebbaf25e652d..3a0c7bd91a73e 100644 --- a/lldb/test/Shell/Commands/command-target-create-resolve-exe.test +++ b/lldb/test/Shell/Commands/command-target-create-resolve-exe.test @@ -1,4 +1,4 @@ -# REQUIRES: system-windows +# REQUIRES: target-windows ## This checks that when starting lldb (or using `target create`) with a ## program name which is on $PATH, or not specify the .exe suffix of a program diff --git a/lldb/test/Shell/Expr/TestAnonNamespaceParamFunc.cpp b/lldb/test/Shell/Expr/TestAnonNamespaceParamFunc.cpp index 3d7b193dc6aac..725b2d637637f 100644 --- a/lldb/test/Shell/Expr/TestAnonNamespaceParamFunc.cpp +++ b/lldb/test/Shell/Expr/TestAnonNamespaceParamFunc.cpp @@ -3,7 +3,7 @@ // linkage. In this case, a function whose argument // is not legally usable outside this TU. -// XFAIL: system-windows +// XFAIL: target-windows // RUN: %build %s -o %t // RUN: %lldb %t -o run -o "expression func(a)" -o exit | FileCheck %s diff --git a/lldb/test/Shell/Expr/TestExecProgress.test b/lldb/test/Shell/Expr/TestExecProgress.test new file mode 100644 index 0000000000000..3f0ae9ffe5633 --- /dev/null +++ b/lldb/test/Shell/Expr/TestExecProgress.test @@ -0,0 +1,8 @@ +# RUN: %lldb -s %s | FileCheck %s +log enable lldb event +expr 1 +expr 1 // And a very long comment. +quit + +# CHECK: {{title = "Running expression", details = "1"}} +# CHECK: {{title = "Running expression", details = "1 // And a ver…"}} diff --git a/lldb/test/Shell/Expr/TestIRMemoryMapWindows.test b/lldb/test/Shell/Expr/TestIRMemoryMapWindows.test index ae29492c9ccc9..66f0bce304730 100644 --- a/lldb/test/Shell/Expr/TestIRMemoryMapWindows.test +++ b/lldb/test/Shell/Expr/TestIRMemoryMapWindows.test @@ -1,4 +1,4 @@ -# REQUIRES: system-windows +# REQUIRES: target-windows # RUN: %clang_cl_host /Zi /GS- %p/Inputs/call-function.cpp /c /o %t.obj # RUN: %msvc_link /debug:full %t.obj /out:%t diff --git a/lldb/test/Shell/Process/Windows/exception_access_violation.cpp b/lldb/test/Shell/Process/Windows/exception_access_violation.cpp index 4835b498ee4df..93898a1991bc6 100644 --- a/lldb/test/Shell/Process/Windows/exception_access_violation.cpp +++ b/lldb/test/Shell/Process/Windows/exception_access_violation.cpp @@ -1,6 +1,6 @@ // clang-format off -// REQUIRES: system-windows +// REQUIRES: target-windows // RUN: %build --compiler=clang-cl -o %t.exe -- %s // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -o "run" -- write | FileCheck --check-prefix=WRITE %s // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -o "run" -- read | FileCheck --check-prefix=READ %s diff --git a/lldb/test/Shell/Process/Windows/process_load.cpp b/lldb/test/Shell/Process/Windows/process_load.cpp index 43bf45865f9ba..de3b4afc77f87 100644 --- a/lldb/test/Shell/Process/Windows/process_load.cpp +++ b/lldb/test/Shell/Process/Windows/process_load.cpp @@ -1,6 +1,6 @@ // clang-format off -// REQUIRES: system-windows +// REQUIRES: target-windows // RUN: %build --compiler=clang-cl -o %t.exe -- %s // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -o "b main" -o "process launch" -o "process load kernel32.dll" | FileCheck %s diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/MyOptional.cpp b/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/MyOptional.cpp new file mode 100644 index 0000000000000..abba34439d88f --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/MyOptional.cpp @@ -0,0 +1,23 @@ +// A bare-bones llvm::Optional reimplementation. + +template struct MyOptionalStorage { + MyOptionalStorage(T val) : value(val), hasVal(true) {} + MyOptionalStorage() {} + T value; + bool hasVal = false; +}; + +template struct MyOptional { + MyOptionalStorage Storage; + MyOptional(T val) : Storage(val) {} + MyOptional() {} + T &operator*() { return Storage.value; } +}; + +void stop() {} + +int main(int argc, char **argv) { + MyOptional x, y = 42; + stop(); // break here + return *y; +} diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/formatter.py b/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/formatter.py new file mode 100644 index 0000000000000..cc8778bd51c74 --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Python/Inputs/FormatterBytecode/formatter.py @@ -0,0 +1,167 @@ +""" +This is the llvm::Optional data formatter from llvm/utils/lldbDataFormatters.py +with the implementation replaced by bytecode. +""" + +from __future__ import annotations +from formatter_bytecode import * +import lldb + + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand( + "type synthetic add -w llvm " + f"-l {__name__}.MyOptionalSynthProvider " + '-x "^MyOptional<.+>$"' + ) + debugger.HandleCommand( + "type summary add -w llvm " + f"-e -F {__name__}.MyOptionalSummaryProvider " + '-x "^MyOptional<.+>$"' + ) + + +def stringify(bytecode: bytearray) -> str: + s = "" + in_hex = False + for b in bytecode: + if (b < 32 or b > 127 or chr(b) in ['"', "`", "'"]) or ( + in_hex + and chr(b).lower() + in [ + "a", + "b", + "c", + "d", + "e", + "f", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ] + ): + s += r"\x" + hex(b)[2:] + in_hex = True + else: + s += chr(b) + in_hex = False + return s + + +def evaluate(assembler: str, data: list): + bytecode = compile(assembler) + trace = True + if trace: + print( + "Compiled to {0} bytes of bytecode:\n{1}".format( + len(bytecode), stringify(bytecode) + ) + ) + result = interpret(bytecode, [], data, False) # trace) + if trace: + print("--> {0}".format(result)) + return result + + +# def GetOptionalValue(valobj): +# storage = valobj.GetChildMemberWithName("Storage") +# if not storage: +# storage = valobj +# +# failure = 2 +# hasVal = storage.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure) +# if hasVal == failure: +# return "" +# +# if hasVal == 0: +# return None +# +# underlying_type = storage.GetType().GetTemplateArgumentType(0) +# storage = storage.GetChildMemberWithName("value") +# return storage.Cast(underlying_type) + + +def MyOptionalSummaryProvider(valobj, internal_dict): + # val = GetOptionalValue(valobj) + # if val is None: + # return "None" + # if val.summary: + # return val.summary + # return val.GetValue() + summary = "" + summary += ' dup "Storage" @get_child_with_name call' # valobj storage + summary += " dup is_null ~ { swap } if drop" # storage + summary += ' dup "hasVal" @get_child_with_name call' # storage obj(hasVal) + summary += ' dup is_null { drop "" } {' + summary += " @get_value_as_unsigned call" # storage int(hasVal) + summary += ' 0u = { "None" } {' + summary += " dup @get_type call" + summary += " 0u @get_template_argument_type call" # storage type + summary += " swap" # type storage + summary += ' "value" @get_child_with_name call' # type value + summary += " swap @cast call" # type(value) + summary += ' dup is_null { "None" } {' + summary += ( + " dup @summary call dup @strlen call { @get_value call } { drop } ifelse" + ) + summary += " } ifelse" + summary += " } ifelse" + summary += " } ifelse" + return evaluate(summary, [valobj]) + + +class MyOptionalSynthProvider: + """Provides deref support to llvm::Optional""" + + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def num_children(self): + # return self.valobj.num_children + num_children = " @get_num_children call" + return evaluate(num_children, [self.valobj]) + + def get_child_index(self, name): + # if name == "$$dereference$$": + # return self.valobj.num_children + # return self.valobj.GetIndexOfChildWithName(name) + get_child_index = ' dup "$$dereference$$" =' + get_child_index += " { drop @get_num_children call } {" # obj name + get_child_index += " @get_child_index call" # index + get_child_index += " } ifelse" + return evaluate(get_child_index, [self.valobj, name]) + + def get_child_at_index(self, index): + # if index < self.valobj.num_children: + # return self.valobj.GetChildAtIndex(index) + # return GetOptionalValue(self.valobj) or lldb.SBValue() + get_child_at_index = " over over swap" # obj index index obj + get_child_at_index += " @get_num_children call" # obj index index n + get_child_at_index += " < { @get_child_at_index call } {" # obj index + + get_opt_val = ' dup "Storage" @get_child_with_name call' # valobj storage + get_opt_val += " dup { swap } if drop" # storage + get_opt_val += ' dup "hasVal" @get_child_with_name call' # storage + get_opt_val += " @get_value_as_unsigned call" # storage int(hasVal) + get_opt_val += ' dup 2 = { drop "" } {' + get_opt_val += ' 0 = { "None" } {' + get_opt_val += ( + " dup @get_type call 0 @get_template_argument_type call" # storage type + ) + get_opt_val += " swap" # type storage + get_opt_val += ' "value" @get_child_with_name call' # type value + get_opt_val += " swap @cast call" # type(value) + get_opt_val += " } ifelse" + get_opt_val += " } ifelse" + + get_child_at_index += get_opt_val + get_child_at_index += " } ifelse" + + return evaluate(get_child_at_index, [self.valobj, index]) diff --git a/lldb/test/Shell/ScriptInterpreter/Python/bytecode.test b/lldb/test/Shell/ScriptInterpreter/Python/bytecode.test new file mode 100644 index 0000000000000..2745680292663 --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Python/bytecode.test @@ -0,0 +1,16 @@ +# RUN: %python %S/../../../../examples/python/formatter_bytecode.py --test +# RUN: %python %S/../../../../examples/python/formatter_bytecode.py --compile "1u dup" | FileCheck %s --check-prefix=COMPILE +# RUN: %python %S/../../../../examples/python/formatter_bytecode.py --disassemble "200101" | FileCheck %s --check-prefix=DISASSEMBLE +# COMPILE: 200101 +# DISASSEMBLE: 1u dup + +# RUN: %clang_host -std=c++17 -g %S/Inputs/FormatterBytecode/MyOptional.cpp -o %t.exe +# RUN: %lldb %t.exe -o "command script import %S/../../../../examples/python/formatter_bytecode.py" -o "command script import %S/Inputs/FormatterBytecode/formatter.py" -o "b -p here" -o "r" -o "v x" -o "v y" -o q | FileCheck %s --check-prefix=OPTIONAL +# OPTIONAL: (lldb) v x +# OPTIONAL: (MyOptional) x = { +# OPTIONAL: hasVal = false +# OPTIONAL: } +# OPTIONAL: (lldb) v y +# OPTIONAL: (MyOptional) y = { +# OPTIONAL: Storage = (value = 42, hasVal = true) +# OPTIONAL: } diff --git a/lldb/test/Shell/SwiftREPL/lit.local.cfg b/lldb/test/Shell/SwiftREPL/lit.local.cfg index 29d485ea63774..539418112b925 100644 --- a/lldb/test/Shell/SwiftREPL/lit.local.cfg +++ b/lldb/test/Shell/SwiftREPL/lit.local.cfg @@ -2,3 +2,14 @@ config.suffixes = ['.test'] if 'lldb-repro' in config.available_features: config.unsupported = True + +def check_exists(path): + import os + if not os.path.isfile(path): + lit_config.warning(f"Runtime dependency not found: {path}") + +# Check runtime dependencies for SwiftREPL tests on Windows +if sys.platform == "win32": + host_arch = config.host_triple.split("-")[0] + check_exists(f"{config.swift_libs_dir}/windows/{host_arch}/swiftrt.obj") + check_exists(f"{config.llvm_shlib_dir}/swiftCore.dll") diff --git a/lldb/test/Shell/SymbolFile/DWARF/packed.cpp b/lldb/test/Shell/SymbolFile/DWARF/packed.cpp index 56a4308ff7c5e..6a794a012a6e5 100644 --- a/lldb/test/Shell/SymbolFile/DWARF/packed.cpp +++ b/lldb/test/Shell/SymbolFile/DWARF/packed.cpp @@ -1,4 +1,4 @@ -// XFAIL: system-windows +// XFAIL: target-windows // RUN: %clangxx_host -gdwarf -o %t %s // RUN: %lldb %t \ // RUN: -o "expr alignof(packed)" \ diff --git a/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp b/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp index 9aa25adf6bcc7..df6ad8c306597 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp @@ -1,6 +1,6 @@ // clang-format off -// REQUIRES: system-windows +// REQUIRES: target-windows // RUN: %build -o %t.exe -- %s // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ // RUN: %p/Inputs/local-variables.lldbinit 2>&1 | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp b/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp index e96e3ed6a0107..d1b9c6dba3ca3 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp @@ -1,5 +1,5 @@ // clang-format off -// REQUIRES: lld, system-windows +// REQUIRES: lld, target-windows // RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ diff --git a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test index 07dc89d460293..7dabf9157d47e 100644 --- a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test +++ b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld, (target-arm || target-aarch64) +REQUIRES: target-windows, lld, (target-arm || target-aarch64) RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp RUN: lldb-test symbols -dump-ast %t.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/class-layout.test b/lldb/test/Shell/SymbolFile/PDB/class-layout.test index c99a180f4f632..efd52b8876ce0 100644 --- a/lldb/test/Shell/SymbolFile/PDB/class-layout.test +++ b/lldb/test/Shell/SymbolFile/PDB/class-layout.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, msvc +REQUIRES: target-windows, msvc RUN: %build --compiler=clang-cl --mode=compile --arch=32 --nodefaultlib --output=%T/ClassLayoutTest.cpp.obj %S/Inputs/ClassLayoutTest.cpp RUN: %build --compiler=msvc --mode=link --arch=32 --nodefaultlib --output=%T/ClassLayoutTest.cpp.exe %T/ClassLayoutTest.cpp.obj RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/compilands.test b/lldb/test/Shell/SymbolFile/PDB/compilands.test index ecee5eb50d399..1847d8d88b266 100644 --- a/lldb/test/Shell/SymbolFile/PDB/compilands.test +++ b/lldb/test/Shell/SymbolFile/PDB/compilands.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, msvc +REQUIRES: target-windows, msvc RUN: %build --compiler=clang-cl --mode=compile --arch=32 --nodefaultlib --output=%T/CompilandsTest.cpp.obj %S/Inputs/CompilandsTest.cpp RUN: %build --compiler=msvc --mode=link --arch=32 --nodefaultlib --output=%T/CompilandsTest.cpp.exe %T/CompilandsTest.cpp.obj RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %T/CompilandsTest.cpp.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/expressions.test b/lldb/test/Shell/SymbolFile/PDB/expressions.test index 89d7c94e7aa06..1932be74ca878 100644 --- a/lldb/test/Shell/SymbolFile/PDB/expressions.test +++ b/lldb/test/Shell/SymbolFile/PDB/expressions.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, msvc +REQUIRES: target-windows, msvc RUN: %build --compiler=msvc --nodefaultlib --output=%t.exe %S/Inputs/ExpressionsTest.cpp RUN: not %lldb -b -s %S/Inputs/ExpressionsTest0.script -s %S/Inputs/ExpressionsTest1.script -s %S/Inputs/ExpressionsTest2.script -- %t.exe 2>&1 | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/func-symbols.test b/lldb/test/Shell/SymbolFile/PDB/func-symbols.test index 5990952938e67..95e0dd5eb078e 100644 --- a/lldb/test/Shell/SymbolFile/PDB/func-symbols.test +++ b/lldb/test/Shell/SymbolFile/PDB/func-symbols.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld +REQUIRES: target-windows, lld RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%T/FuncSymbolsTest.exe %S/Inputs/FuncSymbolsTestMain.cpp %S/Inputs/FuncSymbols.cpp RUN: lldb-test symbols %T/FuncSymbolsTest.exe | FileCheck --check-prefix=CHECK-ONE %s RUN: lldb-test symbols %T/FuncSymbolsTest.exe | FileCheck --check-prefix=CHECK-TWO %s diff --git a/lldb/test/Shell/SymbolFile/PDB/function-level-linking.test b/lldb/test/Shell/SymbolFile/PDB/function-level-linking.test index ec0ef57440070..56343cdbd75bd 100644 --- a/lldb/test/Shell/SymbolFile/PDB/function-level-linking.test +++ b/lldb/test/Shell/SymbolFile/PDB/function-level-linking.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld +REQUIRES: target-windows, lld RUN: %clang_cl_host /c /Zi /Gy %S/Inputs/FunctionLevelLinkingTest.cpp /o %t.obj RUN: lld-link /debug:full /nodefaultlib /entry:main /order:@%S/Inputs/FunctionLevelLinkingTest.ord %t.obj /out:%t.exe RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -verify %t.exe diff --git a/lldb/test/Shell/SymbolFile/PDB/pointers.test b/lldb/test/Shell/SymbolFile/PDB/pointers.test index a3e6f557fed6f..355b5fa16839a 100644 --- a/lldb/test/Shell/SymbolFile/PDB/pointers.test +++ b/lldb/test/Shell/SymbolFile/PDB/pointers.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, msvc +REQUIRES: target-windows, msvc RUN: %build --compiler=clang-cl --mode=compile --arch=32 --nodefaultlib --output=%T/PointerTypeTest.cpp.obj %S/Inputs/PointerTypeTest.cpp RUN: %build --compiler=msvc --mode=link --arch=32 --nodefaultlib --output=%T/PointerTypeTest.cpp.exe %T/PointerTypeTest.cpp.obj RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/type-quals.test b/lldb/test/Shell/SymbolFile/PDB/type-quals.test index cf65c79223b23..982bb70f3c6c5 100644 --- a/lldb/test/Shell/SymbolFile/PDB/type-quals.test +++ b/lldb/test/Shell/SymbolFile/PDB/type-quals.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, msvc +REQUIRES: target-windows, msvc RUN: %build --compiler=clang-cl --mode=compile --arch=32 --nodefaultlib --output=%T/TypeQualsTest.cpp.obj %S/Inputs/TypeQualsTest.cpp RUN: %build --compiler=msvc --mode=link --arch=32 --nodefaultlib --output=%T/TypeQualsTest.cpp.exe %T/TypeQualsTest.cpp.obj RUN: lldb-test symbols %T/TypeQualsTest.cpp.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/udt-layout.test b/lldb/test/Shell/SymbolFile/PDB/udt-layout.test index 84414cbf8440d..bc68539e25ec1 100644 --- a/lldb/test/Shell/SymbolFile/PDB/udt-layout.test +++ b/lldb/test/Shell/SymbolFile/PDB/udt-layout.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld +REQUIRES: target-windows, lld RUN: %build --compiler=clang-cl --output=%t.exe %S/Inputs/UdtLayoutTest.cpp RUN: %lldb -b -s %S/Inputs/UdtLayoutTest.script -- %t.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/variables-locations.test b/lldb/test/Shell/SymbolFile/PDB/variables-locations.test index b5bfc6fe81af9..f8ba4f25ca96f 100644 --- a/lldb/test/Shell/SymbolFile/PDB/variables-locations.test +++ b/lldb/test/Shell/SymbolFile/PDB/variables-locations.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld +REQUIRES: target-windows, lld RUN: %build --compiler=clang-cl --output=%t.exe %S/Inputs/VariablesLocationsTest.cpp RUN: env LLDB_USE_NATIVE_PDB_READER=0 %lldb -b -s %S/Inputs/VariablesLocationsTest.script -- %t.exe | FileCheck %s RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -b -s %S/Inputs/VariablesLocationsTest.script -- %t.exe | FileCheck %s diff --git a/lldb/test/Shell/SymbolFile/PDB/vbases.test b/lldb/test/Shell/SymbolFile/PDB/vbases.test index 57239e07c87d6..b58e3edc3cc80 100644 --- a/lldb/test/Shell/SymbolFile/PDB/vbases.test +++ b/lldb/test/Shell/SymbolFile/PDB/vbases.test @@ -1,4 +1,4 @@ -REQUIRES: system-windows, lld +REQUIRES: target-windows, lld RUN: %build --compiler=clang-cl --output=%t.exe %S/Inputs/VBases.cpp RUN: %lldb -b -s %S/Inputs/VBases.script -- %t.exe | FileCheck %s diff --git a/lldb/test/Shell/Target/dependent-modules-nodupe-windows.test b/lldb/test/Shell/Target/dependent-modules-nodupe-windows.test index 52e46cd0c9b21..78d7f7469b9f8 100644 --- a/lldb/test/Shell/Target/dependent-modules-nodupe-windows.test +++ b/lldb/test/Shell/Target/dependent-modules-nodupe-windows.test @@ -1,4 +1,4 @@ -# REQUIRES: system-windows +# REQUIRES: target-windows # Checks that dependent modules preloaded by LLDB are not duplicated when the # process actually loads the DLL. diff --git a/lldb/test/Shell/Unwind/windows-unaligned-x86_64.test b/lldb/test/Shell/Unwind/windows-unaligned-x86_64.test index 94f1c011ebd2a..0356960424328 100644 --- a/lldb/test/Shell/Unwind/windows-unaligned-x86_64.test +++ b/lldb/test/Shell/Unwind/windows-unaligned-x86_64.test @@ -17,7 +17,7 @@ breakpoint set -n func # CHECK: Breakpoint 1: where = {{.*}}`{{(::)?}}func process launch -# CHECK: stop reason = breakpoint 1.1 +# CHECK: stop reason = breakpoint 1 thread backtrace # CHECK: frame #0: {{.*}}`{{(::)?}}func diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py index d9702f21641b6..66f7d33e7de9e 100644 --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -149,7 +149,16 @@ def use_support_substitutions(config): sdk_path = lit.util.to_string(out) llvm_config.lit_config.note("using SDKROOT: %r" % sdk_path) host_flags += ["-isysroot", sdk_path] - elif sys.platform != "win32": + elif sys.platform == "win32": + # Required in SwiftREPL tests + sdk_path = os.environ.get("SDKROOT") + if sdk_path: + llvm_config.lit_config.note(f"using SDKROOT: {sdk_path}") + llvm_config.with_environment("SDKROOT", sdk_path) + else: + llvm_config.lit_config.warning( + "mandatory environment variable not found: SDKROOT") + else: host_flags += ["-pthread"] config.target_shared_library_suffix = ( diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py index 223612ba205b4..860a755dff100 100644 --- a/lldb/test/Shell/lit.cfg.py +++ b/lldb/test/Shell/lit.cfg.py @@ -87,6 +87,9 @@ if re.match(r"^arm(hf.*-linux)|(.*-linux-gnuabihf)", config.target_triple): config.available_features.add("armhf-linux") +if re.match(r".*-(windows|mingw32)", config.target_triple): + config.available_features.add("target-windows") + if re.match(r".*-(windows-msvc)$", config.target_triple): config.available_features.add("windows-msvc") diff --git a/lldb/test/Shell/lit.site.cfg.py.in b/lldb/test/Shell/lit.site.cfg.py.in index 5e4e2676219f3..d0b8a6be6f9cd 100644 --- a/lldb/test/Shell/lit.site.cfg.py.in +++ b/lldb/test/Shell/lit.site.cfg.py.in @@ -17,6 +17,7 @@ config.cmake_sysroot = lit_config.substitute("@CMAKE_SYSROOT@") config.target_triple = "@LLVM_TARGET_TRIPLE@" config.python_executable = "@Python3_EXECUTABLE@" config.swiftc = "@LLDB_SWIFTC@" +config.swift_libs_dir = '@LLDB_SWIFT_LIBS@' config.lldb_enable_swift = @LLDB_ENABLE_SWIFT_SUPPORT@ config.have_zlib = @LLVM_ENABLE_ZLIB@ config.objc_gnustep_dir = "@LLDB_TEST_OBJC_GNUSTEP_DIR@" diff --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt index d40c357e3f463..949963fd40346 100644 --- a/lldb/unittests/Core/CMakeLists.txt +++ b/lldb/unittests/Core/CMakeLists.txt @@ -3,7 +3,6 @@ add_lldb_unittest(LLDBCoreTests DiagnosticEventTest.cpp DumpDataExtractorTest.cpp DumpRegisterInfoTest.cpp - FileSpecListTest.cpp FormatEntityTest.cpp MangledTest.cpp ModuleSpecTest.cpp diff --git a/lldb/unittests/Core/FileSpecListTest.cpp b/lldb/unittests/Core/FileSpecListTest.cpp deleted file mode 100644 index e63f4a00bc3a9..0000000000000 --- a/lldb/unittests/Core/FileSpecListTest.cpp +++ /dev/null @@ -1,125 +0,0 @@ -//===-- FileSpecListTest.cpp ----------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "gtest/gtest.h" - -#include "lldb/Utility/FileSpecList.h" - -using namespace lldb_private; - -static FileSpec PosixSpec(llvm::StringRef path) { - return FileSpec(path, FileSpec::Style::posix); -} - -static FileSpec WindowsSpec(llvm::StringRef path) { - return FileSpec(path, FileSpec::Style::windows); -} - -TEST(SupportFileListTest, RelativePathMatchesPosix) { - - const FileSpec fullpath = PosixSpec("/build/src/main.cpp"); - const FileSpec relative = PosixSpec("./src/main.cpp"); - const FileSpec basename = PosixSpec("./main.cpp"); - const FileSpec full_wrong = PosixSpec("/other/wrong/main.cpp"); - const FileSpec rel_wrong = PosixSpec("./wrong/main.cpp"); - // Make sure these don't match "src/main.cpp" as we want to match full - // directories only - const FileSpec rel2_wrong = PosixSpec("asrc/main.cpp"); - const FileSpec rel3_wrong = PosixSpec("rc/main.cpp"); - - SupportFileList files; - files.Append(fullpath); - files.Append(relative); - files.Append(basename); - files.Append(full_wrong); - files.Append(rel_wrong); - files.Append(rel2_wrong); - files.Append(rel3_wrong); - - // Make sure the full path only matches the first entry - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath)); - // Make sure the relative path matches the all of the entries that contain - // the relative path - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative)); - - // Make sure looking file a file using the basename matches all entries - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename)); - EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename)); - EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename)); - EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename)); - EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename)); - - // Make sure that paths that have a common suffix don't return values that - // don't match on directory delimiters. - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong)); - EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong)); - - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong)); - EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong)); -} - -TEST(SupportFileListTest, RelativePathMatchesWindows) { - - const FileSpec fullpath = WindowsSpec(R"(C:\build\src\main.cpp)"); - const FileSpec relative = WindowsSpec(R"(.\src\main.cpp)"); - const FileSpec basename = WindowsSpec(R"(.\main.cpp)"); - const FileSpec full_wrong = WindowsSpec(R"(\other\wrong\main.cpp)"); - const FileSpec rel_wrong = WindowsSpec(R"(.\wrong\main.cpp)"); - // Make sure these don't match "src\main.cpp" as we want to match full - // directories only - const FileSpec rel2_wrong = WindowsSpec(R"(asrc\main.cpp)"); - const FileSpec rel3_wrong = WindowsSpec(R"("rc\main.cpp)"); - - SupportFileList files; - files.Append(fullpath); - files.Append(relative); - files.Append(basename); - files.Append(full_wrong); - files.Append(rel_wrong); - files.Append(rel2_wrong); - files.Append(rel3_wrong); - - // Make sure the full path only matches the first entry - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath)); - // Make sure the relative path matches the all of the entries that contain - // the relative path - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative)); - - // Make sure looking file a file using the basename matches all entries - EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename)); - EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename)); - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename)); - EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename)); - EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename)); - EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename)); - EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename)); - - // Make sure that paths that have a common suffix don't return values that - // don't match on directory delimiters. - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong)); - EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong)); - EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong)); - - EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong)); - EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong)); -} diff --git a/lldb/unittests/DataFormatter/CMakeLists.txt b/lldb/unittests/DataFormatter/CMakeLists.txt index 9d967a72bfd1f..b858db1c71634 100644 --- a/lldb/unittests/DataFormatter/CMakeLists.txt +++ b/lldb/unittests/DataFormatter/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(LLDBFormatterTests FormatManagerTests.cpp FormattersContainerTest.cpp + FormatterBytecodeTest.cpp StringPrinterTests.cpp LINK_LIBS diff --git a/lldb/unittests/DataFormatter/FormatterBytecodeTest.cpp b/lldb/unittests/DataFormatter/FormatterBytecodeTest.cpp new file mode 100644 index 0000000000000..7307db650c162 --- /dev/null +++ b/lldb/unittests/DataFormatter/FormatterBytecodeTest.cpp @@ -0,0 +1,264 @@ +#include "DataFormatters/FormatterBytecode.h" +#include "lldb/Utility/StreamString.h" + +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace lldb; +using namespace FormatterBytecode; +using llvm::StringRef; + +namespace { +class FormatterBytecodeTest : public ::testing::Test {}; + +bool Interpret(std::vector code, DataStack &data) { + auto buf = + StringRef(reinterpret_cast(code.data()), code.size()); + std::vector control({buf}); + if (auto error = Interpret(control, data, sel_summary)) { +#ifndef NDEBUG + llvm::errs() << llvm::toString(std::move(error)) << '\n'; +#else + llvm::consumeError(std::move(error)); +#endif + return false; + } + return true; +} + +} // namespace + +TEST_F(FormatterBytecodeTest, StackOps) { + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 23, op_dup, op_plus}, data)); + ASSERT_EQ(data.Pop(), 46u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_drop}, data)); + ASSERT_EQ(data.size(), 0u); + } + { + for (unsigned char i = 0; i < 3; ++i) { + DataStack data; + + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_lit_uint, 2, + op_lit_uint, i, op_pick}, + data)); + ASSERT_EQ(data.Pop(), i); + } + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_over}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_swap}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_EQ(data.Pop(), 1u); + } + { + DataStack data; + ASSERT_TRUE(Interpret( + {op_lit_uint, 0, op_lit_uint, 1, op_lit_uint, 2, op_rot}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_EQ(data.Pop(), 2u); + } +} + +TEST_F(FormatterBytecodeTest, ControlOps) { + { + DataStack data; + ASSERT_TRUE( + Interpret({op_lit_uint, 0, op_begin, 2, op_lit_uint, 42, op_if}, data)); + ASSERT_EQ(data.size(), 0u); + } + { + DataStack data; + ASSERT_TRUE( + Interpret({op_lit_uint, 1, op_begin, 2, op_lit_uint, 42, op_if}, data)); + ASSERT_EQ(data.Pop(), 42u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_begin, 2, op_lit_uint, 42, + op_begin, 2, op_lit_uint, 23, op_ifelse}, + data)); + ASSERT_EQ(data.Pop(), 23u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_begin, 2, op_lit_uint, 42, + op_begin, 2, op_lit_uint, 23, op_ifelse}, + data)); + ASSERT_EQ(data.Pop(), 42u); + } + { + DataStack data(lldb::ValueObjectSP{}); + ASSERT_TRUE(Interpret({op_is_null}, data)); + ASSERT_EQ(data.Pop(), 1u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1u, op_as_int}, data)); + ASSERT_EQ(data.Pop(), 1); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_int, 126, op_as_uint}, data)); + ASSERT_EQ(data.Pop(), ~1ULL); + } +} + +TEST_F(FormatterBytecodeTest, ArithOps) { + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 2, op_lit_uint, 3, op_plus}, data)); + ASSERT_EQ(data.Pop(), 5u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 3, op_lit_uint, 2, op_minus}, data)); + ASSERT_EQ(data.Pop(), 1u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 3, op_lit_uint, 2, op_mul}, data)); + ASSERT_EQ(data.Pop(), 6u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 6, op_lit_uint, 2, op_div}, data)); + ASSERT_EQ(data.Pop(), 3u); + } + { + DataStack data; + ASSERT_FALSE(Interpret({op_lit_uint, 23, op_lit_uint, 0, op_div}, data)); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 2, op_shl}, data)); + ASSERT_EQ(data.Pop(), 4u); + } + { + DataStack data; + unsigned char minus_one = 127; + ASSERT_FALSE( + Interpret({op_lit_int, minus_one, op_lit_uint, 2, op_shl}, data)); + unsigned char minus_two = 126; + ASSERT_TRUE( + Interpret({op_lit_int, minus_two, op_lit_uint, 1, op_shr}, data)); + ASSERT_EQ(data.Pop(), -1); + } + { + DataStack data; + ASSERT_FALSE(Interpret({op_lit_uint, 1, op_lit_uint, 65, op_shl}, data)); + ASSERT_FALSE(Interpret({op_lit_uint, 1, op_lit_uint, 65, op_shr}, data)); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_and}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_and}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_or}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_or}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_or}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_xor}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_xor}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_xor}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_not}, data)); + ASSERT_EQ(data.Pop(), 0xffffffffffffffff); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_eq}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_eq}, data)); + ASSERT_EQ(data.Pop(), 1u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_neq}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_neq}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_lt}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_lt}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_lt}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_gt}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_gt}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_gt}, data)); + ASSERT_EQ(data.Pop(), 0u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_le}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_le}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_le}, data)); + ASSERT_EQ(data.Pop(), 1u); + } + { + DataStack data; + ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_ge}, data)); + ASSERT_EQ(data.Pop(), 0u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_ge}, data)); + ASSERT_EQ(data.Pop(), 1u); + ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_ge}, data)); + ASSERT_EQ(data.Pop(), 1u); + } +} + +TEST_F(FormatterBytecodeTest, CallOps) { + { + DataStack data; + data.Push(std::string{"hello"}); + ASSERT_TRUE(Interpret({op_lit_selector, sel_strlen, op_call}, data)); + ASSERT_EQ(data.Pop(), 5u); + } + { + DataStack data; + data.Push(std::string{"A"}); + data.Push(std::string{"B"}); + data.Push(std::string{"{1}{0}"}); + ASSERT_TRUE(Interpret({op_lit_selector, sel_fmt, op_call}, data)); + ASSERT_EQ(data.Pop(), "BA"); + } + { + DataStack data; + data.Push(std::string{"{0}"}); + ASSERT_FALSE(Interpret({op_lit_selector, sel_fmt, op_call}, data)); + } +} diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt index d10e93ca6336d..a64876184de16 100644 --- a/lldb/unittests/Target/CMakeLists.txt +++ b/lldb/unittests/Target/CMakeLists.txt @@ -11,6 +11,7 @@ add_lldb_unittest(TargetTests RegisterFlagsTest.cpp RemoteAwarePlatformTest.cpp StackFrameRecognizerTest.cpp + SummaryStatisticsTest.cpp FindFileTest.cpp LINK_LIBS diff --git a/lldb/unittests/Target/SummaryStatisticsTest.cpp b/lldb/unittests/Target/SummaryStatisticsTest.cpp new file mode 100644 index 0000000000000..f403375284540 --- /dev/null +++ b/lldb/unittests/Target/SummaryStatisticsTest.cpp @@ -0,0 +1,58 @@ +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private-enumerations.h" +#include "lldb/lldb-private.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include + +using namespace lldb_private; +using Duration = std::chrono::duration; + +class DummySummaryImpl : public lldb_private::TypeSummaryImpl { +public: + DummySummaryImpl() + : TypeSummaryImpl(TypeSummaryImpl::Kind::eSummaryString, + TypeSummaryImpl::Flags()) {} + + std::string GetName() override { return "DummySummary"; } + + std::string GetSummaryKindName() override { return "dummy"; } + + std::string GetDescription() override { return ""; } + + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options) override { + return false; + } +}; + +TEST(MultithreadFormatting, Multithread) { + SummaryStatisticsCache statistics_cache; + DummySummaryImpl summary; + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back(std::thread([&statistics_cache, &summary]() { + auto sp = statistics_cache.GetSummaryStatisticsForProvider(summary); + { + SummaryStatistics::SummaryInvocation invocation(sp); + std::this_thread::sleep_for(Duration(1)); + } + })); + } + + for (auto &thread : threads) + thread.join(); + + auto sp = statistics_cache.GetSummaryStatisticsForProvider(summary); + ASSERT_TRUE(sp->GetDurationReference().get().count() > 10); + ASSERT_TRUE(sp->GetSummaryCount() == 10); + + std::string stats_as_json; + llvm::raw_string_ostream ss(stats_as_json); + ss << sp->ToJSON(); + ASSERT_THAT(stats_as_json, ::testing::HasSubstr("\"name\":\"DummySummary\"")); + ASSERT_THAT(stats_as_json, ::testing::HasSubstr("\"type\":\"dummy\"")); +} diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt index 15f9907363b43..3a73bdc3e9e1a 100644 --- a/lldb/unittests/Utility/CMakeLists.txt +++ b/lldb/unittests/Utility/CMakeLists.txt @@ -13,6 +13,7 @@ add_lldb_unittest(UtilityTests DiagnosticsRenderingTest.cpp EnvironmentTest.cpp EventTest.cpp + FileSpecListTest.cpp FileSpecTest.cpp FlagsTest.cpp ListenerTest.cpp @@ -23,6 +24,7 @@ add_lldb_unittest(UtilityTests ProcessInstanceInfoTest.cpp RangeMapTest.cpp RangeTest.cpp + RealpathPrefixesTest.cpp RegisterValueTest.cpp RegularExpressionTest.cpp ScalarTest.cpp @@ -50,6 +52,7 @@ add_lldb_unittest(UtilityTests XcodeSDKTest.cpp LINK_LIBS + lldbTarget lldbUtility lldbUtilityHelpers LLVMTestingSupport diff --git a/lldb/unittests/Utility/FileSpecListTest.cpp b/lldb/unittests/Utility/FileSpecListTest.cpp new file mode 100644 index 0000000000000..d3f89ad0dfcb3 --- /dev/null +++ b/lldb/unittests/Utility/FileSpecListTest.cpp @@ -0,0 +1,330 @@ +//===-- FileSpecListTest.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "MockSymlinkFileSystem.h" +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Utility/RealpathPrefixes.h" + +using namespace lldb_private; + +static FileSpec PosixSpec(llvm::StringRef path) { + return FileSpec(path, FileSpec::Style::posix); +} + +static FileSpec WindowsSpec(llvm::StringRef path) { + return FileSpec(path, FileSpec::Style::windows); +} + +TEST(SupportFileListTest, RelativePathMatchesPosix) { + + const FileSpec fullpath = PosixSpec("/build/src/main.cpp"); + const FileSpec relative = PosixSpec("./src/main.cpp"); + const FileSpec basename = PosixSpec("./main.cpp"); + const FileSpec full_wrong = PosixSpec("/other/wrong/main.cpp"); + const FileSpec rel_wrong = PosixSpec("./wrong/main.cpp"); + // Make sure these don't match "src/main.cpp" as we want to match full + // directories only + const FileSpec rel2_wrong = PosixSpec("asrc/main.cpp"); + const FileSpec rel3_wrong = PosixSpec("rc/main.cpp"); + + SupportFileList files; + files.Append(fullpath); + files.Append(relative); + files.Append(basename); + files.Append(full_wrong); + files.Append(rel_wrong); + files.Append(rel2_wrong); + files.Append(rel3_wrong); + + // Make sure the full path only matches the first entry + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath)); + // Make sure the relative path matches the all of the entries that contain + // the relative path + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative)); + + // Make sure looking file a file using the basename matches all entries + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename)); + EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename)); + EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename)); + EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename)); + EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename)); + + // Make sure that paths that have a common suffix don't return values that + // don't match on directory delimiters. + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong)); + EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong)); + + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong)); + EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong)); +} + +TEST(SupportFileListTest, RelativePathMatchesWindows) { + + const FileSpec fullpath = WindowsSpec(R"(C:\build\src\main.cpp)"); + const FileSpec relative = WindowsSpec(R"(.\src\main.cpp)"); + const FileSpec basename = WindowsSpec(R"(.\main.cpp)"); + const FileSpec full_wrong = WindowsSpec(R"(\other\wrong\main.cpp)"); + const FileSpec rel_wrong = WindowsSpec(R"(.\wrong\main.cpp)"); + // Make sure these don't match "src\main.cpp" as we want to match full + // directories only + const FileSpec rel2_wrong = WindowsSpec(R"(asrc\main.cpp)"); + const FileSpec rel3_wrong = WindowsSpec(R"("rc\main.cpp)"); + + SupportFileList files; + files.Append(fullpath); + files.Append(relative); + files.Append(basename); + files.Append(full_wrong); + files.Append(rel_wrong); + files.Append(rel2_wrong); + files.Append(rel3_wrong); + + // Make sure the full path only matches the first entry + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath)); + // Make sure the relative path matches the all of the entries that contain + // the relative path + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative)); + + // Make sure looking file a file using the basename matches all entries + EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename)); + EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename)); + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename)); + EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename)); + EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename)); + EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename)); + EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename)); + + // Make sure that paths that have a common suffix don't return values that + // don't match on directory delimiters. + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong)); + EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong)); + EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong)); + + EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong)); + EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong)); +} + +// Support file is a symlink to the breakpoint file. +// Absolute paths are used. +// A matching prefix is set. +// Should find it compatible. +TEST(SupportFileListTest, SymlinkedAbsolutePaths) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/symlink_dir/foo.h"), PosixSpec("/real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("/symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("/real_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, (size_t)0); +} + +// Support file is a symlink to the breakpoint file. +// Absolute paths are used. +// A matching prefix is set, which is the root directory. +// Should find it compatible. +TEST(SupportFileListTest, RootDirectory) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/symlink_dir/foo.h"), PosixSpec("/real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("/symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("/real_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, (size_t)0); +} + +// Support file is a symlink to the breakpoint file. +// Relative paths are used. +// A matching prefix is set. +// Should find it compatible. +TEST(SupportFileListTest, SymlinkedRelativePaths) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("symlink_dir/foo.h"), PosixSpec("real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("real_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, (size_t)0); +} + +// Support file is a symlink to the breakpoint file. +// A matching prefix is set. +// Input file only match basename and not directory. +// Should find it incompatible. +TEST(SupportFileListTest, RealpathOnlyMatchFileName) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("symlink_dir/foo.h"), PosixSpec("real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("some_other_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, UINT32_MAX); +} + +// Support file is a symlink to the breakpoint file. +// A prefix is set, which is a matching string prefix, but not a path prefix. +// Should find it incompatible. +TEST(SupportFileListTest, DirectoryMatchStringPrefixButNotWholeDirectoryName) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("symlink_dir/foo.h"), PosixSpec("real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("symlink")); // This is a string prefix of the + // symlink but not a path prefix. + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("real_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, UINT32_MAX); +} + +// Support file is a symlink to the breakpoint file. +// A matching prefix is set. +// However, the breakpoint is set with a partial path. +// Should find it compatible. +TEST(SupportFileListTest, PartialBreakpointPath) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("symlink_dir/foo.h"), PosixSpec("/real_dir/foo.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("real_dir/foo.h"), &prefixes); + EXPECT_EQ(ret, (size_t)0); +} + +// Support file is a symlink to the breakpoint file. +// A matching prefix is set. +// However, the basename is different between the symlink and its target. +// Should find it incompatible. +TEST(SupportFileListTest, DifferentBasename) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/symlink_dir/foo.h"), PosixSpec("/real_dir/bar.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("/symlink_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("real_dir/bar.h"), &prefixes); + EXPECT_EQ(ret, UINT32_MAX); +} + +// No prefixes are configured. +// The support file and the breakpoint file are different. +// Should find it incompatible. +TEST(SupportFileListTest, NoPrefixes) { + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("/real_dir/bar.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("/real_dir/foo.h"), nullptr); + EXPECT_EQ(ret, UINT32_MAX); +} + +// No prefixes are configured. +// The support file and the breakpoint file are the same. +// Should find it compatible. +TEST(SupportFileListTest, SameFile) { + // Prepare support file list + SupportFileList support_file_list; + support_file_list.Append(PosixSpec("/real_dir/foo.h")); + + // Test + size_t ret = support_file_list.FindCompatibleIndex( + 0, PosixSpec("/real_dir/foo.h"), nullptr); + EXPECT_EQ(ret, (size_t)0); +} diff --git a/lldb/unittests/Utility/MockSymlinkFileSystem.h b/lldb/unittests/Utility/MockSymlinkFileSystem.h new file mode 100644 index 0000000000000..7fa1f93bfa38a --- /dev/null +++ b/lldb/unittests/Utility/MockSymlinkFileSystem.h @@ -0,0 +1,66 @@ +//===-- MockSymlinkFileSystem.h +//--------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/FileSpec.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace lldb_private { + +// A mock file system that realpath's a given symlink to a given realpath. +class MockSymlinkFileSystem : public llvm::vfs::FileSystem { +public: + // Treat all files as non-symlinks. + MockSymlinkFileSystem() = default; + + /// Treat \a symlink as a symlink to \a realpath. Treat all other files as + /// non-symlinks. + MockSymlinkFileSystem(FileSpec &&symlink, FileSpec &&realpath, + FileSpec::Style style = FileSpec::Style::native) + : m_symlink(std::move(symlink)), m_realpath(std::move(realpath)), + m_style(style) {} + + /// If \a Path matches the symlink given in the ctor, put the realpath given + /// in the ctor into \a Output. + std::error_code getRealPath(const llvm::Twine &Path, + llvm::SmallVectorImpl &Output) override { + if (FileSpec(Path.str(), m_style) == m_symlink) { + std::string path = m_realpath.GetPath(); + Output.assign(path.begin(), path.end()); + } else { + Path.toVector(Output); + } + return {}; + } + + // Implement the rest of the interface + llvm::ErrorOr status(const llvm::Twine &Path) override { + return llvm::errc::operation_not_permitted; + } + llvm::ErrorOr> + openFileForRead(const llvm::Twine &Path) override { + return llvm::errc::operation_not_permitted; + } + llvm::vfs::directory_iterator dir_begin(const llvm::Twine &Dir, + std::error_code &EC) override { + return llvm::vfs::directory_iterator(); + } + std::error_code setCurrentWorkingDirectory(const llvm::Twine &Path) override { + return llvm::errc::operation_not_permitted; + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return llvm::errc::operation_not_permitted; + } + +private: + FileSpec m_symlink; + FileSpec m_realpath; + FileSpec::Style m_style; +}; + +} // namespace lldb_private diff --git a/lldb/unittests/Utility/RealpathPrefixesTest.cpp b/lldb/unittests/Utility/RealpathPrefixesTest.cpp new file mode 100644 index 0000000000000..a4dc7d6dd9822 --- /dev/null +++ b/lldb/unittests/Utility/RealpathPrefixesTest.cpp @@ -0,0 +1,153 @@ +//===-- RealpathPrefixesTest.cpp +//--------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "MockSymlinkFileSystem.h" +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Utility/RealpathPrefixes.h" + +using namespace lldb_private; + +static FileSpec PosixSpec(llvm::StringRef path) { + return FileSpec(path, FileSpec::Style::posix); +} + +static FileSpec WindowsSpec(llvm::StringRef path) { + return FileSpec(path, FileSpec::Style::windows); +} + +// Should resolve a symlink which match an absolute prefix +TEST(RealpathPrefixesTest, MatchingAbsolutePrefix) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/dir1/link.h"), PosixSpec("/dir2/real.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/dir1")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("/dir1/link.h")); + EXPECT_EQ(ret, PosixSpec("/dir2/real.h")); +} + +// Should resolve a symlink which match a relative prefix +TEST(RealpathPrefixesTest, MatchingRelativePrefix) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("dir1/link.h"), PosixSpec("dir2/real.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("dir1")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("dir1/link.h")); + EXPECT_EQ(ret, PosixSpec("dir2/real.h")); +} + +// Should resolve in Windows and/or with a case-insensitive support file +TEST(RealpathPrefixesTest, WindowsAndCaseInsensitive) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + WindowsSpec("f:\\dir1\\link.h"), WindowsSpec("f:\\dir2\\real.h"), + FileSpec::Style::windows)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(WindowsSpec("f:\\dir1")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(WindowsSpec("F:\\DIR1\\LINK.H")); + EXPECT_EQ(ret, WindowsSpec("f:\\dir2\\real.h")); +} + +// Should resolve a symlink when there is mixture of matching and mismatching +// prefixex +TEST(RealpathPrefixesTest, MatchingAndMismatchingPrefix) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/dir1/link.h"), PosixSpec("/dir2/real.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/fake/path1")); + file_spec_list.Append(PosixSpec("/dir1")); // Matching prefix + file_spec_list.Append(PosixSpec("/fake/path2")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("/dir1/link.h")); + EXPECT_EQ(ret, PosixSpec("/dir2/real.h")); +} + +// Should resolve a symlink when the prefixes matches after normalization +TEST(RealpathPrefixesTest, ComplexPrefixes) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("dir1/link.h"), PosixSpec("dir2/real.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append( + PosixSpec("./dir1/foo/../bar/..")); // Equivalent to "/dir1" + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("dir1/link.h")); + EXPECT_EQ(ret, PosixSpec("dir2/real.h")); +} + +// Should not resolve a symlink which doesn't match any prefixes +TEST(RealpathPrefixesTest, MismatchingPrefixes) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs(new MockSymlinkFileSystem( + PosixSpec("/dir1/link.h"), PosixSpec("/dir2/real.h"), + FileSpec::Style::posix)); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/dir3")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("/dir1/link.h")); + EXPECT_EQ(ret, std::nullopt); +} + +// Should not resolve a realpath +TEST(RealpathPrefixesTest, Realpath) { + // Prepare FS + llvm::IntrusiveRefCntPtr fs( + new MockSymlinkFileSystem()); + + // Prepare RealpathPrefixes + FileSpecList file_spec_list; + file_spec_list.Append(PosixSpec("/symlink_dir")); + RealpathPrefixes prefixes(file_spec_list, fs); + + // Test + std::optional ret = + prefixes.ResolveSymlinks(PosixSpec("/dir/real.h")); + EXPECT_EQ(ret, std::nullopt); +} diff --git a/lldb/utils/lldb-dotest/CMakeLists.txt b/lldb/utils/lldb-dotest/CMakeLists.txt index 4980c3521b831..6b30e3c0cbb07 100644 --- a/lldb/utils/lldb-dotest/CMakeLists.txt +++ b/lldb/utils/lldb-dotest/CMakeLists.txt @@ -34,6 +34,7 @@ set(vars LLDB_TEST_EXECUTABLE LLDB_TEST_COMPILER LLDB_TEST_DSYMUTIL + LLDB_TEST_MAKE LLDB_LIBS_DIR LLVM_TOOLS_DIR LLDB_SWIFTC diff --git a/lldb/utils/lldb-dotest/lldb-dotest.in b/lldb/utils/lldb-dotest/lldb-dotest.in index 941a98ef5876a..c8b253d70c2b6 100755 --- a/lldb/utils/lldb-dotest/lldb-dotest.in +++ b/lldb/utils/lldb-dotest/lldb-dotest.in @@ -10,6 +10,7 @@ executable = '@LLDB_TEST_EXECUTABLE_CONFIGURED@' compiler = '@LLDB_TEST_COMPILER_CONFIGURED@' swift_compiler = '@LLDB_SWIFTC@' dsymutil = '@LLDB_TEST_DSYMUTIL_CONFIGURED@' +make = '@LLDB_TEST_MAKE_CONFIGURED@' lldb_build_dir = '@LLDB_TEST_BUILD_DIRECTORY_CONFIGURED@' lldb_build_intel_pt = "@LLDB_BUILD_INTEL_PT@" lldb_framework_dir = "@LLDB_FRAMEWORK_DIR_CONFIGURED@" @@ -37,6 +38,7 @@ if __name__ == '__main__': cmd.extend(['--executable', executable]) cmd.extend(['--compiler', compiler]) cmd.extend(['--dsymutil', dsymutil]) + cmd.extend(['--make', make]) cmd.extend(['--lldb-libs-dir', lldb_libs_dir]) cmd.extend(['--llvm-tools-dir', llvm_tools_dir]) if swift_compiler: diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index efecad660f5ac..1eabe9678096b 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -910,9 +910,9 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND NOT LLVM_ENABLE_WARNINGS) append("-w" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() -# Enable '-index-store-path' on a Debug build, if the compiler supports it and for non-IDE generators. -option(LLVM_DISABLE_INDEX_STORE "Disable '-index-store-path' flag" Off) -if (NOT LLVM_DISABLE_INDEX_STORE AND NOT XCODE AND NOT MSVC_IDE AND uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") +# Allow enabling '-index-store-path' for non-IDE generators if the compiler supports it. +option(LLVM_ENABLE_INDEX_STORE "Enable '-index-store-path' flag" Off) +if (LLVM_ENABLE_INDEX_STORE AND NOT XCODE AND NOT MSVC_IDE) set(INDEX_DATA_STORE_PATH "${PROJECT_BINARY_DIR}/IndexStore" CACHE STRING "Index store path") check_c_compiler_flag("-Werror -index-store-path \"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/IndexStore\"" "C_SUPPORTS_INDEX_STORE") diff --git a/llvm/docs/CommandGuide/llvm-remarkutil.rst b/llvm/docs/CommandGuide/llvm-remarkutil.rst index af7d8eb31c018..4816da451d8f5 100644 --- a/llvm/docs/CommandGuide/llvm-remarkutil.rst +++ b/llvm/docs/CommandGuide/llvm-remarkutil.rst @@ -21,6 +21,7 @@ Subcommands * :ref:`yaml2bitstream_subcommand` - Reserialize YAML remarks to bitstream. * :ref:`instruction-count_subcommand` - Output function instruction counts. * :ref:`annotation-count_subcommand` - Output remark type count from annotation remarks. + * :ref:`bounds-safety-count_subcommand` - Output the total count of -fbounds-safety checks for the given binary. * :ref:`size-diff_subcommand` - Compute diff in size remarks. .. _bitstream2yaml_subcommand: @@ -204,6 +205,39 @@ Options * ``analysis-aliasing`` * ``failure`` + +.. _bounds-safety-count_subcommand: + +bounds-safety-count +~~~~~~~~~~~~~~~~~ + +.. program:: llvm-remarkutil bounds-safety-count + +USAGE: :program:`llvm-remarkutil` bounds-safety-count --parser= [--collect-per-source] -o + +Summary +^^^^^^^ + +Outputs the total -fbounds-safety remarks count for the given binary using `bounds-safety-total-summary` remark information. +If `collect-per-source` flag is provided the count will be broken down by `source` utilizing DebugLoc information. +bounds-safety-total-summary require bounds-safety-xxx remarks. + +CSV format is as follows: + +Collecting count for the binary + +:: + Count + 123 + +Using `collect-per-source` + +:: + Source, Count + /path/to/file1, 123 + /path/to/file2 233 + + .. _size-diff_subcommand: size-diff diff --git a/llvm/include/llvm/Analysis/InlineCost.h b/llvm/include/llvm/Analysis/InlineCost.h index c5978ce54fc18..36bc5c21c67d8 100644 --- a/llvm/include/llvm/Analysis/InlineCost.h +++ b/llvm/include/llvm/Analysis/InlineCost.h @@ -319,6 +319,7 @@ std::optional getInliningCostEstimate( CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref GetAssumptionCache, function_ref GetBFI = nullptr, + function_ref GetTLI = nullptr, ProfileSummaryInfo *PSI = nullptr, OptimizationRemarkEmitter *ORE = nullptr); @@ -328,6 +329,7 @@ std::optional getInliningCostFeatures( CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref GetAssumptionCache, function_ref GetBFI = nullptr, + function_ref GetTLI = nullptr, ProfileSummaryInfo *PSI = nullptr, OptimizationRemarkEmitter *ORE = nullptr); diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h index d9bfca763819f..df23c70e25514 100644 --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1307,6 +1307,25 @@ class ScalarEvolution { LoopGuards(ScalarEvolution &SE) : SE(SE) {} + /// Recursively collect loop guards in \p Guards, starting from + /// block \p Block with predecessor \p Pred. The intended starting point + /// is to collect from a loop header and its predecessor. + static void + collectFromBlock(ScalarEvolution &SE, ScalarEvolution::LoopGuards &Guards, + const BasicBlock *Block, const BasicBlock *Pred, + SmallPtrSetImpl &VisitedBlocks, + unsigned Depth = 0); + + /// Collect loop guards in \p Guards, starting from PHINode \p + /// Phi, by calling \p collectFromBlock on the incoming blocks of + /// \Phi and trying to merge the found constraints into a single + /// combined one for \p Phi. + static void collectFromPHI( + ScalarEvolution &SE, ScalarEvolution::LoopGuards &Guards, + const PHINode &Phi, SmallPtrSetImpl &VisitedBlocks, + SmallDenseMap &IncomingGuards, + unsigned Depth); + public: /// Collect rewrite map for loop guards for loop \p L, together with flags /// indicating if NUW and NSW can be preserved during rewriting. diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h index 2411b2b31d293..66b025a640b4c 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfo.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -614,6 +614,9 @@ class TargetTransformInfo { unsigned MaxIterationsCountToAnalyze; /// Don't disable runtime unroll for the loops which were vectorized. bool UnrollVectorizedLoop = false; + /// Don't allow runtime unrolling if expanding the trip count takes more + /// than SCEVExpansionBudget. + unsigned SCEVExpansionBudget; }; /// Get target-customized preferences for the generic loop unrolling diff --git a/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.def b/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.def new file mode 100644 index 0000000000000..40a7c06089909 --- /dev/null +++ b/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.def @@ -0,0 +1,58 @@ +//===- BoundsSafetyOptRemarks.def ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file declares the different types of BoundsSafety missed remarks +//===----------------------------------------------------------------------===// + +/* TO_UPSTREAM(BoundsSafety) ON*/ +#ifndef BOUNDS_SAFETY_MISSED_REMARK +#error BOUNDS_SAFETY_MISSED_REMARK must be defined +#endif + +// BOUNDS_SAFETY_MISSED_REMARK( +// , +// , +// ) +// +// - This should be a valid enum identifier. The name of this +// field indicates the type of missed optimization opportunity. +// +// - First part of the tuple to annotate an +// instruction. Represents the type of missed optimization opportunity and has +// the same value as . +// +// - Second part of the tuple to +// annotate an instruction. It is an explanation to the -fbounds-safety adopters to +// rewrite their code to optimize the checks away. + +BOUNDS_SAFETY_MISSED_REMARK( + PHI_DIRECTION, "bounds-safety-missed-optimization-phi-direction", + "Cannot remove bounds checks because the pointer induction variable and " + "loop counter are not stepping in the same direction. Consider rewriting " + "the loop counter to step in the same direction as the pointer induction " + "variable to help the optimizer remove the access bound checks.") + +BOUNDS_SAFETY_MISSED_REMARK( + PHI_STEP_SIZE, "bounds-safety-missed-optimization-phi-step-size", + "Cannot remove bound checks because the pointer induction variable and " + "loop counter don't have the same step size. Consider rewriting the loop " + "counter to have the same step size as the pointer induction variable to " + "help the optimizer remove the access bound checks") + +BOUNDS_SAFETY_MISSED_REMARK( + MISS_NUW_FLAG, "bounds-safety-missed-optimization-nuw", + "Check can not be removed because the arithmetic operation might wrap in " + "the unsigned sense. Optimize the check by adding conditions to check for " + "overflow before doing the operation") + +BOUNDS_SAFETY_MISSED_REMARK( + MISS_NSW_FLAG, "bounds-safety-missed-optimization-nsw", + "Check can not be removed because the arithmetic operation might wrap in " + "the signed sense. Optimize the check by adding conditions to check for " + "overflow before doing the operation") + +/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file diff --git a/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.h b/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.h new file mode 100644 index 0000000000000..f4874705a6b79 --- /dev/null +++ b/llvm/include/llvm/Remarks/BoundsSafetyOptRemarks.h @@ -0,0 +1,45 @@ +//===--- BoundsSafetyOptRemarks.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +/* TO_UPSTREAM(BoundsSafety) ON*/ +#ifndef LLVM_CODEGEN_BOUNDS_SAFETY_MISSED_OPT_REMARKS_H +#define LLVM_CODEGEN_BOUNDS_SAFETY_MISSED_OPT_REMARKS_H +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" + +enum BoundsSafetyOptRemarkKind { +#define BOUNDS_SAFETY_MISSED_REMARK(SUFFIX, ANNOTATION_STR, ACTIONABLE_STR) \ + BNS_MISSED_REMARK_##SUFFIX, +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_MISSED_REMARK +}; + +namespace llvm { +/// Annotate instruction with -fbounds-safety missed remarks and additonal context +/// based on `BoundsSafetyOptRemarkKind` entry. +inline void annotate(Instruction *I, BoundsSafetyOptRemarkKind Kind) { + switch (Kind) { +#define BOUNDS_SAFETY_MISSED_REMARK(SUFFIX, ANNOTATION_STR, ACTIONABLE_STR) \ + case BNS_MISSED_REMARK_##SUFFIX: \ + return I->addAnnotationMetadata({ANNOTATION_STR, ACTIONABLE_STR}); +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_MISSED_REMARK + } + llvm_unreachable("Unhandled BoundsSafetyOptRemarkKind"); +} +/// Return the list of runtime checks associated with instruction I. +SmallVector getBoundsSafetyRuntimeChecks(Instruction *I); + +/// Annotate runtime checks associated with instruction I. +void annotateRuntimeChecks(Instruction *I, BoundsSafetyOptRemarkKind Remark); + +/// Return true if instruction \p I has bounds-safety-check annotation. +bool isBoundsSafetyAnnotated(Instruction *I); +} // namespace llvm +#endif +/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td index 46044aab79a83..e7895258438d2 100644 --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -231,6 +231,10 @@ def SDTCatchret : SDTypeProfile<0, 2, [ // catchret SDTCisVT<0, OtherVT>, SDTCisVT<1, OtherVT> ]>; +def SDTCleanupret : SDTypeProfile<0, 1, [ // cleanupret + SDTCisVT<0, OtherVT> +]>; + def SDTNone : SDTypeProfile<0, 0, []>; // ret, trap def SDTUBSANTrap : SDTypeProfile<0, 1, []>; // ubsantrap @@ -680,7 +684,7 @@ def brind : SDNode<"ISD::BRIND" , SDTBrind, [SDNPHasChain]>; def br : SDNode<"ISD::BR" , SDTBr, [SDNPHasChain]>; def catchret : SDNode<"ISD::CATCHRET" , SDTCatchret, [SDNPHasChain, SDNPSideEffect]>; -def cleanupret : SDNode<"ISD::CLEANUPRET" , SDTNone, [SDNPHasChain]>; +def cleanupret : SDNode<"ISD::CLEANUPRET" , SDTCleanupret, [SDNPHasChain]>; def trap : SDNode<"ISD::TRAP" , SDTNone, [SDNPHasChain, SDNPSideEffect]>; diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h index 17927d4214369..63f0f94e9e714 100644 --- a/llvm/include/llvm/TargetParser/Triple.h +++ b/llvm/include/llvm/TargetParser/Triple.h @@ -561,6 +561,11 @@ class Triple { bool isOSzOS() const { return getOS() == Triple::ZOS; } + /// Is this an Apple MachO triple. + bool isAppleMachO() const { + return (getVendor() == Triple::Apple) && isOSBinFormatMachO(); + } + /// Is this a "Darwin" OS (macOS, iOS, tvOS, watchOS, XROS, or DriverKit). bool isOSDarwin() const { return isMacOSX() || isiOS() || isWatchOS() || isDriverKit() || isXROS(); diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h index ebcbd5d9e8880..67862ff1cbf42 100644 --- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h +++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h @@ -84,6 +84,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner { // combining and will be updated to reflect any changes. LoopInfo *LI; + ReversePostOrderTraversal &RPOT; + bool MadeIRChange = false; /// Edges that are known to never be taken. @@ -92,18 +94,25 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner { /// Order of predecessors to canonicalize phi nodes towards. SmallDenseMap, 8> PredOrder; + /// Backedges, used to avoid pushing instructions across backedges in cases + /// where this may result in infinite combine loops. For irreducible loops + /// this picks an arbitrary backedge. + SmallDenseSet, 8> BackEdges; + bool ComputedBackEdges = false; + public: InstCombiner(InstructionWorklist &Worklist, BuilderTy &Builder, bool MinimizeSize, AAResults *AA, AssumptionCache &AC, TargetLibraryInfo &TLI, TargetTransformInfo &TTI, DominatorTree &DT, OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI, - ProfileSummaryInfo *PSI, const DataLayout &DL, LoopInfo *LI) + ProfileSummaryInfo *PSI, const DataLayout &DL, LoopInfo *LI, + ReversePostOrderTraversal &RPOT) : TTI(TTI), Builder(Builder), Worklist(Worklist), MinimizeSize(MinimizeSize), AA(AA), AC(AC), TLI(TLI), DT(DT), DL(DL), SQ(DL, &TLI, &DT, &AC, nullptr, /*UseInstrInfo*/ true, /*CanUseUndef*/ true, &DC), - ORE(ORE), BFI(BFI), BPI(BPI), PSI(PSI), LI(LI) {} + ORE(ORE), BFI(BFI), BPI(BPI), PSI(PSI), LI(LI), RPOT(RPOT) {} virtual ~InstCombiner() = default; @@ -359,6 +368,13 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner { std::function SimplifyAndSetOp); + void computeBackEdges(); + bool isBackEdge(const BasicBlock *From, const BasicBlock *To) { + if (!ComputedBackEdges) + computeBackEdges(); + return BackEdges.contains({From, To}); + } + /// Inserts an instruction \p New before instruction \p Old /// /// Also adds the new instruction to the worklist and returns \p New so that diff --git a/llvm/include/llvm/Transforms/Scalar/LoopTrapAnalysis.h b/llvm/include/llvm/Transforms/Scalar/LoopTrapAnalysis.h new file mode 100644 index 0000000000000..49f6d7611cb4e --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/LoopTrapAnalysis.h @@ -0,0 +1,23 @@ +//===- LoopTrapAnalysis.h ---------------------------*- C++ -*-------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_LoopTrapAnalysis_H +#define LLVM_TRANSFORMS_SCALAR_LoopTrapAnalysis_H + +#include "llvm/Analysis/LoopAnalysisManager.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +struct LoopTrapAnalysisPass : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_LoopTrapAnalysis_H diff --git a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h index 797c082333a76..8cf17ced458c8 100644 --- a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h +++ b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h @@ -75,6 +75,7 @@ struct UnrollLoopOptions { bool UnrollRemainder; bool ForgetAllSCEV; const Instruction *Heart = nullptr; + unsigned SCEVExpansionBudget; }; LoopUnrollResult UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI, @@ -90,7 +91,7 @@ bool UnrollRuntimeLoopRemainder( bool UseEpilogRemainder, bool UnrollRemainder, bool ForgetAllSCEV, LoopInfo *LI, ScalarEvolution *SE, DominatorTree *DT, AssumptionCache *AC, const TargetTransformInfo *TTI, bool PreserveLCSSA, - Loop **ResultLoop = nullptr); + unsigned SCEVExpansionBudget, Loop **ResultLoop = nullptr); LoopUnrollResult UnrollAndJamLoop(Loop *L, unsigned Count, unsigned TripCount, unsigned TripMultiple, bool UnrollRemainder, diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp index 345e5a0195201..b6b16a818999d 100644 --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -249,6 +249,9 @@ class CallAnalyzer : public InstVisitor { /// Getter for BlockFrequencyInfo function_ref GetBFI; + /// Getter for TargetLibraryInfo + function_ref GetTLI; + /// Profile summary information. ProfileSummaryInfo *PSI; @@ -433,6 +436,7 @@ class CallAnalyzer : public InstVisitor { bool simplifyIntrinsicCallIsConstant(CallBase &CB); bool simplifyIntrinsicCallObjectSize(CallBase &CB); ConstantInt *stripAndComputeInBoundsConstantOffsets(Value *&V); + bool isLoweredToCall(Function *F, CallBase &Call); /// Return true if the given argument to the function being considered for /// inlining has the given attribute set either at the call site or the @@ -492,13 +496,15 @@ class CallAnalyzer : public InstVisitor { bool visitUnreachableInst(UnreachableInst &I); public: - CallAnalyzer(Function &Callee, CallBase &Call, const TargetTransformInfo &TTI, - function_ref GetAssumptionCache, - function_ref GetBFI = nullptr, - ProfileSummaryInfo *PSI = nullptr, - OptimizationRemarkEmitter *ORE = nullptr) + CallAnalyzer( + Function &Callee, CallBase &Call, const TargetTransformInfo &TTI, + function_ref GetAssumptionCache, + function_ref GetBFI = nullptr, + function_ref GetTLI = nullptr, + ProfileSummaryInfo *PSI = nullptr, + OptimizationRemarkEmitter *ORE = nullptr) : TTI(TTI), GetAssumptionCache(GetAssumptionCache), GetBFI(GetBFI), - PSI(PSI), F(Callee), DL(F.getDataLayout()), ORE(ORE), + GetTLI(GetTLI), PSI(PSI), F(Callee), DL(F.getDataLayout()), ORE(ORE), CandidateCall(Call) {} InlineResult analyze(); @@ -687,7 +693,8 @@ class InlineCostCallAnalyzer final : public CallAnalyzer { /// FIXME: if InlineCostCallAnalyzer is derived from, this may need /// to instantiate the derived class. InlineCostCallAnalyzer CA(*F, Call, IndirectCallParams, TTI, - GetAssumptionCache, GetBFI, PSI, ORE, false); + GetAssumptionCache, GetBFI, GetTLI, PSI, ORE, + false); if (CA.analyze().isSuccess()) { // We were able to inline the indirect call! Subtract the cost from the // threshold to get the bonus we want to apply, but don't go below zero. @@ -1105,10 +1112,12 @@ class InlineCostCallAnalyzer final : public CallAnalyzer { const TargetTransformInfo &TTI, function_ref GetAssumptionCache, function_ref GetBFI = nullptr, + function_ref GetTLI = nullptr, ProfileSummaryInfo *PSI = nullptr, OptimizationRemarkEmitter *ORE = nullptr, bool BoostIndirect = true, bool IgnoreThreshold = false) - : CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, PSI, ORE), + : CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, GetTLI, PSI, + ORE), ComputeFullInlineCost(OptComputeFullInlineCost || Params.ComputeFullInlineCost || ORE || isCostBenefitAnalysisEnabled()), @@ -1226,8 +1235,8 @@ class InlineCostFeaturesAnalyzer final : public CallAnalyzer { InlineConstants::IndirectCallThreshold; InlineCostCallAnalyzer CA(*F, Call, IndirectCallParams, TTI, - GetAssumptionCache, GetBFI, PSI, ORE, false, - true); + GetAssumptionCache, GetBFI, GetTLI, PSI, ORE, + false, true); if (CA.analyze().isSuccess()) { increment(InlineCostFeatureIndex::nested_inline_cost_estimate, CA.getCost()); @@ -1353,9 +1362,11 @@ class InlineCostFeaturesAnalyzer final : public CallAnalyzer { const TargetTransformInfo &TTI, function_ref &GetAssumptionCache, function_ref GetBFI, + function_ref GetTLI, ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE, Function &Callee, CallBase &Call) - : CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, PSI) {} + : CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, GetTLI, + PSI) {} const InlineCostFeatures &features() const { return Cost; } }; @@ -2258,6 +2269,44 @@ bool CallAnalyzer::simplifyCallSite(Function *F, CallBase &Call) { return false; } +bool CallAnalyzer::isLoweredToCall(Function *F, CallBase &Call) { + const TargetLibraryInfo *TLI = GetTLI ? &GetTLI(*F) : nullptr; + LibFunc LF; + if (!TLI || !TLI->getLibFunc(*F, LF) || !TLI->has(LF)) + return TTI.isLoweredToCall(F); + + switch (LF) { + case LibFunc_memcpy_chk: + case LibFunc_memmove_chk: + case LibFunc_mempcpy_chk: + case LibFunc_memset_chk: { + // Calls to __memcpy_chk whose length is known to fit within the object + // size will eventually be replaced by inline stores. Therefore, these + // should not incur a call penalty. This is only really relevant on + // platforms whose headers redirect memcpy to __memcpy_chk (e.g. Darwin), as + // other platforms use memcpy intrinsics, which are already exempt from the + // call penalty. + auto *LenOp = dyn_cast(Call.getOperand(2)); + if (!LenOp) + LenOp = dyn_cast_or_null( + SimplifiedValues.lookup(Call.getOperand(2))); + auto *ObjSizeOp = dyn_cast(Call.getOperand(3)); + if (!ObjSizeOp) + ObjSizeOp = dyn_cast_or_null( + SimplifiedValues.lookup(Call.getOperand(3))); + if (LenOp && ObjSizeOp && + LenOp->getLimitedValue() <= ObjSizeOp->getLimitedValue()) { + return false; + } + break; + } + default: + break; + } + + return TTI.isLoweredToCall(F); +} + bool CallAnalyzer::visitCallBase(CallBase &Call) { if (!onCallBaseVisitStart(Call)) return true; @@ -2339,7 +2388,7 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) { return false; } - if (TTI.isLoweredToCall(F)) { + if (isLoweredToCall(F, Call)) { onLoweredCall(F, Call, IsIndirectCall); } @@ -2943,6 +2992,7 @@ std::optional llvm::getInliningCostEstimate( CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref GetAssumptionCache, function_ref GetBFI, + function_ref GetTLI, ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) { const InlineParams Params = {/* DefaultThreshold*/ 0, /*HintThreshold*/ {}, @@ -2956,7 +3006,7 @@ std::optional llvm::getInliningCostEstimate( /*EnableDeferral*/ true}; InlineCostCallAnalyzer CA(*Call.getCalledFunction(), Call, Params, CalleeTTI, - GetAssumptionCache, GetBFI, PSI, ORE, true, + GetAssumptionCache, GetBFI, GetTLI, PSI, ORE, true, /*IgnoreThreshold*/ true); auto R = CA.analyze(); if (!R.isSuccess()) @@ -2968,9 +3018,10 @@ std::optional llvm::getInliningCostFeatures( CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref GetAssumptionCache, function_ref GetBFI, + function_ref GetTLI, ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) { - InlineCostFeaturesAnalyzer CFA(CalleeTTI, GetAssumptionCache, GetBFI, PSI, - ORE, *Call.getCalledFunction(), Call); + InlineCostFeaturesAnalyzer CFA(CalleeTTI, GetAssumptionCache, GetBFI, GetTLI, + PSI, ORE, *Call.getCalledFunction(), Call); auto R = CFA.analyze(); if (!R.isSuccess()) return std::nullopt; @@ -3070,7 +3121,7 @@ InlineCost llvm::getInlineCost( << ")\n"); InlineCostCallAnalyzer CA(*Callee, Call, Params, CalleeTTI, - GetAssumptionCache, GetBFI, PSI, ORE); + GetAssumptionCache, GetBFI, GetTLI, PSI, ORE); InlineResult ShouldInline = CA.analyze(); LLVM_DEBUG(CA.dump()); @@ -3262,7 +3313,8 @@ InlineCostAnnotationPrinterPass::run(Function &F, continue; OptimizationRemarkEmitter ORE(CalledFunction); InlineCostCallAnalyzer ICCA(*CalledFunction, *CI, Params, TTI, - GetAssumptionCache, nullptr, &PSI, &ORE); + GetAssumptionCache, nullptr, nullptr, &PSI, + &ORE); ICCA.analyze(); OS << " Analyzing call of " << CalledFunction->getName() << "... (caller:" << CI->getCaller()->getName() << ")\n"; diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 51cffac808768..90743bd108ed7 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -222,6 +222,10 @@ static cl::opt RangeIterThreshold( cl::desc("Threshold for switching to iteratively computing SCEV ranges"), cl::init(32)); +static cl::opt MaxLoopGuardCollectionDepth( + "scalar-evolution-max-loop-guard-collection-depth", cl::Hidden, + cl::desc("Maximum depth for recrusive loop guard collection"), cl::init(1)); + static cl::opt ClassifyExpressions("scalar-evolution-classify-expressions", cl::Hidden, cl::init(true), @@ -10608,7 +10612,7 @@ ScalarEvolution::getPredecessorWithUniqueSuccessorForBB(const BasicBlock *BB) if (const Loop *L = LI.getLoopFor(BB)) return {L->getLoopPredecessor(), L->getHeader()}; - return {nullptr, nullptr}; + return {nullptr, BB}; } /// SCEV structural equivalence is usually sufficient for testing whether two @@ -15089,7 +15093,81 @@ bool ScalarEvolution::matchURem(const SCEV *Expr, const SCEV *&LHS, ScalarEvolution::LoopGuards ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { + BasicBlock *Header = L->getHeader(); + BasicBlock *Pred = L->getLoopPredecessor(); LoopGuards Guards(SE); + SmallPtrSet VisitedBlocks; + collectFromBlock(SE, Guards, Header, Pred, VisitedBlocks); + return Guards; +} + +void ScalarEvolution::LoopGuards::collectFromPHI( + ScalarEvolution &SE, ScalarEvolution::LoopGuards &Guards, + const PHINode &Phi, SmallPtrSetImpl &VisitedBlocks, + SmallDenseMap &IncomingGuards, + unsigned Depth) { + if (!SE.isSCEVable(Phi.getType())) + return; + + using MinMaxPattern = std::pair; + auto GetMinMaxConst = [&](unsigned IncomingIdx) -> MinMaxPattern { + const BasicBlock *InBlock = Phi.getIncomingBlock(IncomingIdx); + if (!VisitedBlocks.insert(InBlock).second) + return {nullptr, scCouldNotCompute}; + auto [G, Inserted] = IncomingGuards.try_emplace(InBlock, LoopGuards(SE)); + if (Inserted) + collectFromBlock(SE, G->second, Phi.getParent(), InBlock, VisitedBlocks, + Depth + 1); + auto &RewriteMap = G->second.RewriteMap; + if (RewriteMap.empty()) + return {nullptr, scCouldNotCompute}; + auto S = RewriteMap.find(SE.getSCEV(Phi.getIncomingValue(IncomingIdx))); + if (S == RewriteMap.end()) + return {nullptr, scCouldNotCompute}; + auto *SM = dyn_cast_if_present(S->second); + if (!SM) + return {nullptr, scCouldNotCompute}; + if (const SCEVConstant *C0 = dyn_cast(SM->getOperand(0))) + return {C0, SM->getSCEVType()}; + return {nullptr, scCouldNotCompute}; + }; + auto MergeMinMaxConst = [](MinMaxPattern P1, + MinMaxPattern P2) -> MinMaxPattern { + auto [C1, T1] = P1; + auto [C2, T2] = P2; + if (!C1 || !C2 || T1 != T2) + return {nullptr, scCouldNotCompute}; + switch (T1) { + case scUMaxExpr: + return {C1->getAPInt().ult(C2->getAPInt()) ? C1 : C2, T1}; + case scSMaxExpr: + return {C1->getAPInt().slt(C2->getAPInt()) ? C1 : C2, T1}; + case scUMinExpr: + return {C1->getAPInt().ugt(C2->getAPInt()) ? C1 : C2, T1}; + case scSMinExpr: + return {C1->getAPInt().sgt(C2->getAPInt()) ? C1 : C2, T1}; + default: + llvm_unreachable("Trying to merge non-MinMaxExpr SCEVs."); + } + }; + auto P = GetMinMaxConst(0); + for (unsigned int In = 1; In < Phi.getNumIncomingValues(); In++) { + if (!P.first) + break; + P = MergeMinMaxConst(P, GetMinMaxConst(In)); + } + if (P.first) { + const SCEV *LHS = SE.getSCEV(const_cast(&Phi)); + SmallVector Ops({P.first, LHS}); + const SCEV *RHS = SE.getMinMaxExpr(P.second, Ops); + Guards.RewriteMap.insert({LHS, RHS}); + } +} + +void ScalarEvolution::LoopGuards::collectFromBlock( + ScalarEvolution &SE, ScalarEvolution::LoopGuards &Guards, + const BasicBlock *Block, const BasicBlock *Pred, + SmallPtrSetImpl &VisitedBlocks, unsigned Depth) { SmallVector ExprsToRewrite; auto CollectCondition = [&](ICmpInst::Predicate Predicate, const SCEV *LHS, const SCEV *RHS, @@ -15428,14 +15506,13 @@ ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { } }; - BasicBlock *Header = L->getHeader(); SmallVector> Terms; // First, collect information from assumptions dominating the loop. for (auto &AssumeVH : SE.AC.assumptions()) { if (!AssumeVH) continue; auto *AssumeI = cast(AssumeVH); - if (!SE.DT.dominates(AssumeI, Header)) + if (!SE.DT.dominates(AssumeI, Block)) continue; Terms.emplace_back(AssumeI->getOperand(0), true); } @@ -15446,8 +15523,8 @@ ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { if (GuardDecl) for (const auto *GU : GuardDecl->users()) if (const auto *Guard = dyn_cast(GU)) - if (Guard->getFunction() == Header->getParent() && - SE.DT.dominates(Guard, Header)) + if (Guard->getFunction() == Block->getParent() && + SE.DT.dominates(Guard, Block)) Terms.emplace_back(Guard->getArgOperand(0), true); // Third, collect conditions from dominating branches. Starting at the loop @@ -15455,11 +15532,12 @@ ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { // predecessors that can be found that have unique successors leading to the // original header. // TODO: share this logic with isLoopEntryGuardedByCond. - for (std::pair Pair( - L->getLoopPredecessor(), Header); - Pair.first; + unsigned NumCollectedConditions = 0; + VisitedBlocks.insert(Block); + std::pair Pair(Pred, Block); + for (; Pair.first; Pair = SE.getPredecessorWithUniqueSuccessorForBB(Pair.first)) { - + VisitedBlocks.insert(Pair.second); const BranchInst *LoopEntryPredicate = dyn_cast(Pair.first->getTerminator()); if (!LoopEntryPredicate || LoopEntryPredicate->isUnconditional()) @@ -15467,6 +15545,23 @@ ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { Terms.emplace_back(LoopEntryPredicate->getCondition(), LoopEntryPredicate->getSuccessor(0) == Pair.second); + NumCollectedConditions++; + + // If we are recursively collecting guards stop after 2 + // conditions to limit compile-time impact for now. + if (Depth > 0 && NumCollectedConditions == 2) + break; + } + // Finally, if we stopped climbing the predecessor chain because + // there wasn't a unique one to continue, try to collect conditions + // for PHINodes by recursively following all of their incoming + // blocks and try to merge the found conditions to build a new one + // for the Phi. + if (Pair.second->hasNPredecessorsOrMore(2) && + Depth < MaxLoopGuardCollectionDepth) { + SmallDenseMap IncomingGuards; + for (auto &Phi : Pair.second->phis()) + collectFromPHI(SE, Guards, Phi, VisitedBlocks, IncomingGuards, Depth); } // Now apply the information from the collected conditions to @@ -15523,7 +15618,6 @@ ScalarEvolution::LoopGuards::collect(const Loop *L, ScalarEvolution &SE) { Guards.RewriteMap.insert({Expr, Guards.rewrite(RewriteTo)}); } } - return Guards; } const SCEV *ScalarEvolution::LoopGuards::rewrite(const SCEV *Expr) const { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 692ef234c2f1a..2020920d7b7ca 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2168,8 +2168,10 @@ void SelectionDAGBuilder::visitCleanupRet(const CleanupReturnInst &I) { FuncInfo.MBB->normalizeSuccProbs(); // Create the terminator node. - SDValue Ret = - DAG.getNode(ISD::CLEANUPRET, getCurSDLoc(), MVT::Other, getControlRoot()); + MachineBasicBlock *CleanupPadMBB = + FuncInfo.MBBMap[I.getCleanupPad()->getParent()]; + SDValue Ret = DAG.getNode(ISD::CLEANUPRET, getCurSDLoc(), MVT::Other, + getControlRoot(), DAG.getBasicBlock(CleanupPadMBB)); DAG.setRoot(Ret); } diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCOFF.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCOFF.h index f58faebf6a004..a48ab8f96ad22 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCOFF.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCOFF.h @@ -50,9 +50,9 @@ class RuntimeDyldCOFF : public RuntimeDyldImpl { static constexpr StringRef getImportSymbolPrefix() { return "__imp_"; } - bool relocationNeedsDLLImportStub(const RelocationRef &R) const; + bool relocationNeedsDLLImportStub(const RelocationRef &R) const override; - unsigned sizeAfterAddingDLLImportStub(unsigned Size) const { + unsigned sizeAfterAddingDLLImportStub(unsigned Size) const override { return alignTo(Size, PointerSize) + PointerSize; } diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 9da55b8ca0909..64af2f83e9e98 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -246,6 +246,7 @@ #include "llvm/Transforms/Scalar/LoopSimplifyCFG.h" #include "llvm/Transforms/Scalar/LoopSink.h" #include "llvm/Transforms/Scalar/LoopStrengthReduce.h" +#include "llvm/Transforms/Scalar/LoopTrapAnalysis.h" #include "llvm/Transforms/Scalar/LoopUnrollAndJamPass.h" #include "llvm/Transforms/Scalar/LoopUnrollPass.h" #include "llvm/Transforms/Scalar/LoopVersioningLICM.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index bc595869ab869..1f4210960d02c 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -107,6 +107,7 @@ #include "llvm/Transforms/Scalar/LoopRotation.h" #include "llvm/Transforms/Scalar/LoopSimplifyCFG.h" #include "llvm/Transforms/Scalar/LoopSink.h" +#include "llvm/Transforms/Scalar/LoopTrapAnalysis.h" #include "llvm/Transforms/Scalar/LoopUnrollAndJamPass.h" #include "llvm/Transforms/Scalar/LoopUnrollPass.h" #include "llvm/Transforms/Scalar/LoopVersioningLICM.h" @@ -284,6 +285,13 @@ static cl::opt EnableConstraintElimination( cl::desc( "Enable pass to eliminate conditions based on linear constraints")); +/* TO_UPSTREAM(BoundsSafety) ON */ +static cl::opt EnableLoopTrapAnalysis( + "enable-loop-trap-analysis", cl::init(false), cl::Hidden, + cl::desc("Enable pass to emit remarks about loops with " + "checks that can be hoisted out of the loop.")); +/* TO_UPSTREAM(BoundsSafety) OFF */ + static cl::opt AttributorRun( "attributor-enable", cl::Hidden, cl::init(AttributorRunOption::NONE), cl::desc("Enable the attributor inter-procedural deduction pass"), @@ -1596,6 +1604,11 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level, PGOOpt->Action == PGOOptions::SampleUse) MPM.addPass(PseudoProbeUpdatePass()); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -1672,6 +1685,11 @@ PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) { invokeOptimizerEarlyEPCallbacks(MPM, Level); invokeOptimizerLastEPCallbacks(MPM, Level); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -1729,6 +1747,11 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline( MPM.addPass(buildModuleOptimizationPipeline( Level, ThinOrFullLTOPhase::ThinLTOPostLink)); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -1764,6 +1787,11 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -1842,6 +1870,11 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -2054,6 +2087,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, MPM.addPass(CGProfilePass(/*InLTOPostLink=*/true)); invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level); + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); // Emit annotation remarks. addAnnotationRemarksPass(MPM); @@ -2160,6 +2195,8 @@ ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, if (LTOPreLink) addRequiredLTOPreLinkPasses(MPM); + if (EnableLoopTrapAnalysis) + MPM.addPass(createModuleToFunctionPassAdaptor(LoopTrapAnalysisPass())); MPM.addPass(createModuleToFunctionPassAdaptor(AnnotationRemarksPass())); return MPM; diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 4f695d9916bd9..590ff76c7ae00 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -384,6 +384,8 @@ FUNCTION_PASS("loop-fusion", LoopFusePass()) FUNCTION_PASS("loop-load-elim", LoopLoadEliminationPass()) FUNCTION_PASS("loop-simplify", LoopSimplifyPass()) FUNCTION_PASS("loop-sink", LoopSinkPass()) +// TO_UPSTREAM(BoundsSafety) +FUNCTION_PASS("loop-trap-analysis", LoopTrapAnalysisPass()) FUNCTION_PASS("loop-versioning", LoopVersioningPass()) FUNCTION_PASS("lower-allow-check", LowerAllowCheckPass()) FUNCTION_PASS("lower-atomic", LowerAtomicPass()) diff --git a/llvm/lib/Remarks/CMakeLists.txt b/llvm/lib/Remarks/CMakeLists.txt index 9e88ceccd5dbb..38ab5b50e60c5 100644 --- a/llvm/lib/Remarks/CMakeLists.txt +++ b/llvm/lib/Remarks/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_component_library(LLVMRemarks BitstreamRemarkParser.cpp BitstreamRemarkSerializer.cpp + ExtraOptRemarks.cpp Remark.cpp RemarkFormat.cpp RemarkLinker.cpp diff --git a/llvm/lib/Remarks/ExtraOptRemarks.cpp b/llvm/lib/Remarks/ExtraOptRemarks.cpp new file mode 100644 index 0000000000000..9a52daadb5526 --- /dev/null +++ b/llvm/lib/Remarks/ExtraOptRemarks.cpp @@ -0,0 +1,65 @@ +//===--- ExtraOptRemarks.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +/* TO_UPSTREAM(BoundsSafety) ON*/ +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/Remarks/BoundsSafetyOptRemarks.h" + +namespace llvm { + +void annotateRuntimeChecks(Instruction *I, BoundsSafetyOptRemarkKind Remark) { + auto BoundsSafetyChecks = getBoundsSafetyRuntimeChecks(I); + // Early exit if we don't have any -fbounds-safety runtime checks associated with + // the pointer. + if (BoundsSafetyChecks.empty()) + return; + for (auto *CI : BoundsSafetyChecks) + annotate(CI, Remark); +} + +bool isBoundsSafetyAnnotated(Instruction *I) { + if (!I->hasMetadata(LLVMContext::MD_annotation)) + return false; + for (const MDOperand &Op : + I->getMetadata(LLVMContext::MD_annotation)->operands()) { + StringRef S; + if (isa(Op.get())) + S = cast(Op.get())->getString(); + else { + auto *AnnotationTuple = dyn_cast(Op.get()); + S = cast(AnnotationTuple->getOperand(0).get())->getString(); + } + if (S.starts_with("bounds-safety")) { + return true; + } + } + return false; +} + +// Recursively look through the users of I for cmp instructions that contain +// -fbounds-safety annotations. +void searchBoundsSafetyRuntimeChecks(Instruction *I, + SmallVector &Checks, + int Depth) { + if (Depth == 0) + return; + if (isa(I) && isBoundsSafetyAnnotated(I)) + Checks.push_back(I); + + for (auto *User : I->users()) + if (auto *UI = dyn_cast(User)) + searchBoundsSafetyRuntimeChecks(UI, Checks, Depth - 1); +} + +SmallVector getBoundsSafetyRuntimeChecks(Instruction *I) { + SmallVector Checks; + searchBoundsSafetyRuntimeChecks(I, Checks, MaxAnalysisRecursionDepth - 1); + return Checks; +} +} // namespace llvm +/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 9792cc309fb7c..c28240e4b520a 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1484,13 +1484,6 @@ static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( NewOpc = AArch64::LDRQpost; break; } - // Get rid of the SEH code associated with the old instruction. - if (NeedsWinCFI) { - auto SEH = std::next(MBBI); - if (AArch64InstrInfo::isSEHInstruction(*SEH)) - SEH->eraseFromParent(); - } - TypeSize Scale = TypeSize::getFixed(1), Width = TypeSize::getFixed(0); int64_t MinOffset, MaxOffset; bool Success = static_cast(TII)->getMemOpInfo( @@ -1504,16 +1497,27 @@ static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( CSStackSizeInc < MinOffset || CSStackSizeInc > MaxOffset) { // If we are destroying the frame, make sure we add the increment after the // last frame operation. - if (FrameFlag == MachineInstr::FrameDestroy) + if (FrameFlag == MachineInstr::FrameDestroy) { ++MBBI; + // Also skip the SEH instruction, if needed + if (NeedsWinCFI && AArch64InstrInfo::isSEHInstruction(*MBBI)) + ++MBBI; + } emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, StackOffset::getFixed(CSStackSizeInc), TII, FrameFlag, - false, false, nullptr, EmitCFI, + false, NeedsWinCFI, HasWinCFI, EmitCFI, StackOffset::getFixed(CFAOffset)); return std::prev(MBBI); } + // Get rid of the SEH code associated with the old instruction. + if (NeedsWinCFI) { + auto SEH = std::next(MBBI); + if (AArch64InstrInfo::isSEHInstruction(*SEH)) + SEH->eraseFromParent(); + } + MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(NewOpc)); MIB.addReg(AArch64::SP, RegState::Define); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index f91b57f9b9500..2bd059b17ca99 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -5162,7 +5162,7 @@ let isPseudo = 1 in { //===----------------------------------------------------------------------===// let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, isCodeGenOnly = 1, isReturn = 1, isEHScopeReturn = 1, isPseudo = 1 in { - def CLEANUPRET : Pseudo<(outs), (ins), [(cleanupret)]>, Sched<[]>; + def CLEANUPRET : Pseudo<(outs), (ins), [(cleanupret bb)]>, Sched<[]>; let usesCustomInserter = 1 in def CATCHRET : Pseudo<(outs), (ins am_brcond:$dst, am_brcond:$src), [(catchret bb:$dst, bb:$src)]>, Sched<[]>; diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp index fbd20c2fc5824..e9b31af54e3e1 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp @@ -3611,6 +3611,127 @@ getFalkorUnrollingPreferences(Loop *L, ScalarEvolution &SE, } } +/// For Apple CPUs, we want to runtime-unroll loops to make better use if the +/// OOO engine's wide instruction window and various predictors. +static void +getAppleRuntimeUnrollPreferences(Loop *L, ScalarEvolution &SE, + TargetTransformInfo::UnrollingPreferences &UP, + AArch64TTIImpl &TTI) { + // Limit loops with structure that is highly likely to benefit from runtime + // unrolling; that is we exclude outer loops, loops with multiple exits and + // many blocks (i.e. likely with complex control flow). Note that the + // heuristics here may be overly conservative and we err on the side of + // avoiding runtime unrolling rather than unroll excessively. They are all + // subject to further refinement. + if (!L->isInnermost() || !L->getExitBlock() || L->getNumBlocks() > 8) + return; + + const SCEV *BTC = SE.getBackedgeTakenCount(L); + if (isa(BTC) || isa(BTC) || + (SE.getSmallConstantMaxTripCount(L) > 0 && + SE.getSmallConstantMaxTripCount(L) <= 32)) + return; + if (findStringMetadataForLoop(L, "llvm.loop.isvectorized")) + return; + + int64_t Size = 0; + for (auto *BB : L->getBlocks()) { + for (auto &I : *BB) { + if (!isa(&I) && isa(&I)) + return; + SmallVector Operands(I.operand_values()); + Size += + *TTI.getInstructionCost(&I, Operands, TTI::TCK_CodeSize).getValue(); + } + } + + // Limit to loops with trip counts that are cheap to expand. + UP.SCEVExpansionBudget = 1; + + // Try to unroll small, single block loops, if they have load/store + // dependencies, to expose more parallel memory access streams. + BasicBlock *Header = L->getHeader(); + if (Header == L->getLoopLatch()) { + if (Size > 8) + return; + + SmallPtrSet LoadedValues; + SmallVector Stores; + for (auto *BB : L->blocks()) { + for (auto &I : *BB) { + Value *Ptr = getLoadStorePointerOperand(&I); + if (!Ptr) + continue; + const SCEV *PtrSCEV = SE.getSCEV(Ptr); + if (SE.isLoopInvariant(PtrSCEV, L)) + continue; + if (isa(&I)) + LoadedValues.insert(&I); + else + Stores.push_back(cast(&I)); + } + } + + // Try to find an unroll count that maximizes the use of the instruction + // window, i.e. trying to fetch as many instructions per cycle as possible. + unsigned MaxInstsPerLine = 16; + unsigned UC = 1; + unsigned BestUC = 1; + unsigned SizeWithBestUC = BestUC * Size; + while (UC <= 8) { + unsigned SizeWithUC = UC * Size; + if (SizeWithUC > 48) + break; + if ((SizeWithUC % MaxInstsPerLine) == 0 || + (SizeWithBestUC % MaxInstsPerLine) < (SizeWithUC % MaxInstsPerLine)) { + BestUC = UC; + SizeWithBestUC = BestUC * Size; + } + UC++; + } + + if (BestUC == 1 || none_of(Stores, [&LoadedValues](StoreInst *SI) { + return LoadedValues.contains(SI->getOperand(0)); + })) + return; + + UP.Runtime = true; + UP.DefaultUnrollRuntimeCount = BestUC; + return; + } + + // Try to runtime-unroll loops with early-continues depending on loop-varying + // loads; this helps with branch-prediction for the early-continues. + auto *Term = dyn_cast(Header->getTerminator()); + auto *Latch = L->getLoopLatch(); + SmallVector Preds(predecessors(Latch)); + if (!Term || !Term->isConditional() || Preds.size() == 1 || + none_of(Preds, [Header](BasicBlock *Pred) { return Header == Pred; }) || + none_of(Preds, [L](BasicBlock *Pred) { return L->contains(Pred); })) + return; + + std::function DependsOnLoopLoad = + [&](Instruction *I, unsigned Depth) -> bool { + if (isa(I) || L->isLoopInvariant(I) || Depth > 8) + return false; + + if (isa(I)) + return true; + + return any_of(I->operands(), [&](Value *V) { + auto *I = dyn_cast(V); + return I && DependsOnLoopLoad(I, Depth + 1); + }); + }; + CmpInst::Predicate Pred; + Instruction *I; + if (match(Term, m_Br(m_ICmp(Pred, m_Instruction(I), m_Value()), m_Value(), + m_Value())) && + DependsOnLoopLoad(I, 0)) { + UP.Runtime = true; + } +} + void AArch64TTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP, OptimizationRemarkEmitter *ORE) { @@ -3628,9 +3749,21 @@ void AArch64TTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, // Disable partial & runtime unrolling on -Os. UP.PartialOptSizeThreshold = 0; - if (ST->getProcFamily() == AArch64Subtarget::Falkor && - EnableFalkorHWPFUnrollFix) - getFalkorUnrollingPreferences(L, SE, UP); + // Apply subtarget-specific unrolling preferences. + switch (ST->getProcFamily()) { + case AArch64Subtarget::AppleA14: + case AArch64Subtarget::AppleA15: + case AArch64Subtarget::AppleA16: + case AArch64Subtarget::AppleM4: + getAppleRuntimeUnrollPreferences(L, SE, UP, *this); + break; + case AArch64Subtarget::Falkor: + if (EnableFalkorHWPFUnrollFix) + getFalkorUnrollingPreferences(L, SE, UP); + break; + default: + break; + } // Scan the loop: don't unroll loops with calls as this could prevent // inlining. Don't unroll vector loops either, as they don't benefit much from diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 70b91c266c497..cd0aea313da0d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -87,9 +87,8 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass { const MachineBasicBlock *MBB); unsigned getDelegateDepth(const SmallVectorImpl &Stack, const MachineBasicBlock *MBB); - unsigned - getRethrowDepth(const SmallVectorImpl &Stack, - const SmallVectorImpl &EHPadStack); + unsigned getRethrowDepth(const SmallVectorImpl &Stack, + const MachineBasicBlock *EHPadToRethrow); void rewriteDepthImmediates(MachineFunction &MF); void fixEndsAtEndOfFunction(MachineFunction &MF); void cleanupFunctionData(MachineFunction &MF); @@ -1612,34 +1611,13 @@ unsigned WebAssemblyCFGStackify::getDelegateDepth( unsigned WebAssemblyCFGStackify::getRethrowDepth( const SmallVectorImpl &Stack, - const SmallVectorImpl &EHPadStack) { + const MachineBasicBlock *EHPadToRethrow) { unsigned Depth = 0; - // In our current implementation, rethrows always rethrow the exception caught - // by the innermost enclosing catch. This means while traversing Stack in the - // reverse direction, when we encounter END_TRY, we should check if the - // END_TRY corresponds to the current innermost EH pad. For example: - // try - // ... - // catch ;; (a) - // try - // rethrow 1 ;; (b) - // catch ;; (c) - // rethrow 0 ;; (d) - // end ;; (e) - // end ;; (f) - // - // When we are at 'rethrow' (d), while reversely traversing Stack the first - // 'end' we encounter is the 'end' (e), which corresponds to the 'catch' (c). - // And 'rethrow' (d) rethrows the exception caught by 'catch' (c), so we stop - // there and the depth should be 0. But when we are at 'rethrow' (b), it - // rethrows the exception caught by 'catch' (a), so when traversing Stack - // reversely, we should skip the 'end' (e) and choose 'end' (f), which - // corresponds to 'catch' (a). for (auto X : reverse(Stack)) { const MachineInstr *End = X.second; if (End->getOpcode() == WebAssembly::END_TRY) { auto *EHPad = TryToEHPad[EndToBegin[End]]; - if (EHPadStack.back() == EHPad) + if (EHPadToRethrow == EHPad) break; } ++Depth; @@ -1651,7 +1629,6 @@ unsigned WebAssemblyCFGStackify::getRethrowDepth( void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { // Now rewrite references to basic blocks to be depth immediates. SmallVector Stack; - SmallVector EHPadStack; for (auto &MBB : reverse(MF)) { for (MachineInstr &MI : llvm::reverse(MBB)) { switch (MI.getOpcode()) { @@ -1669,31 +1646,14 @@ void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { break; case WebAssembly::END_BLOCK: + case WebAssembly::END_TRY: Stack.push_back(std::make_pair(&MBB, &MI)); break; - case WebAssembly::END_TRY: { - // We handle DELEGATE in the default level, because DELEGATE has - // immediate operands to rewrite. - Stack.push_back(std::make_pair(&MBB, &MI)); - auto *EHPad = TryToEHPad[EndToBegin[&MI]]; - EHPadStack.push_back(EHPad); - break; - } - case WebAssembly::END_LOOP: Stack.push_back(std::make_pair(EndToBegin[&MI]->getParent(), &MI)); break; - case WebAssembly::CATCH: - case WebAssembly::CATCH_ALL: - EHPadStack.pop_back(); - break; - - case WebAssembly::RETHROW: - MI.getOperand(0).setImm(getRethrowDepth(Stack, EHPadStack)); - break; - default: if (MI.isTerminator()) { // Rewrite MBB operands to be depth immediates. @@ -1705,6 +1665,9 @@ void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { if (MI.getOpcode() == WebAssembly::DELEGATE) MO = MachineOperand::CreateImm( getDelegateDepth(Stack, MO.getMBB())); + else if (MI.getOpcode() == WebAssembly::RETHROW) + MO = MachineOperand::CreateImm( + getRethrowDepth(Stack, MO.getMBB())); else MO = MachineOperand::CreateImm( getBranchDepth(Stack, MO.getMBB())); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 0f06f54f219f9..18545e92886ac 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -245,6 +245,19 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { ReplaceNode(Node, Throw); return; } + case Intrinsic::wasm_rethrow: { + // RETHROW's BB argument will be populated in LateEHPrepare. Just use a + // '0' as a placeholder for now. + MachineSDNode *Rethrow = CurDAG->getMachineNode( + WebAssembly::RETHROW, DL, + MVT::Other, // outchain type + { + CurDAG->getConstant(0, DL, MVT::i32), // placeholder + Node->getOperand(0) // inchain + }); + ReplaceNode(Node, Rethrow); + return; + } } break; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td index be6547007aaf7..261277f8a02cf 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -132,11 +132,9 @@ let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { defm THROW : I<(outs), (ins tag_op:$tag, variable_ops), (outs), (ins tag_op:$tag), [], "throw \t$tag", "throw \t$tag", 0x08>; -defm RETHROW : NRI<(outs), (ins i32imm:$depth), [], "rethrow \t$depth", 0x09>; +// $ehpad is the EH pad where the exception to rethrow has been caught. +defm RETHROW : NRI<(outs), (ins bb_op:$ehpad), [], "rethrow \t$ehpad", 0x09>; } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 -// The depth argument will be computed in CFGStackify. We set it to 0 here for -// now. -def : Pat<(int_wasm_rethrow), (RETHROW 0)>; // Region within which an exception is caught: try / end_try let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in { @@ -160,7 +158,8 @@ defm DELEGATE : NRI<(outs), (ins bb_op:$dst), [], "delegate \t $dst", 0x18>; // Pseudo instructions: cleanupret / catchret let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, isPseudo = 1, isEHScopeReturn = 1 in { - defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "cleanupret", 0>; + defm CLEANUPRET : NRI<(outs), (ins bb_op:$ehpad), [(cleanupret bb:$ehpad)], + "cleanupret", 0>; defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from), [(catchret bb:$dst, bb:$from)], "catchret", 0>; } // isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp index 94037b9ab189d..b8f3bcb57f6bf 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp @@ -245,11 +245,39 @@ bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) { Changed = true; break; } + case WebAssembly::RETHROW: + // These RETHROWs here were lowered from llvm.wasm.rethrow() intrinsics, + // generated in Clang for when an exception is not caught by the given + // type (e.g. catch (int)). + // + // RETHROW's BB argument is the EH pad where the exception to rethrow has + // been caught. (Until this point, RETHROW has just a '0' as a placeholder + // argument.) For these llvm.wasm.rethrow()s, we can safely assume the + // exception comes from the nearest dominating EH pad, because catch.start + // EH pad is structured like this: + // + // catch.start: + // catchpad ... + // %matches = compare ehselector with typeid + // br i1 %matches, label %catch, label %rethrow + // + // rethrow: + // ;; rethrows the exception caught in 'catch.start' + // call @llvm.wasm.rethrow() + TI->removeOperand(0); + TI->addOperand(MachineOperand::CreateMBB(getMatchingEHPad(TI))); + Changed = true; + break; case WebAssembly::CLEANUPRET: { - // Replace a cleanupret with a rethrow. For C++ support, currently - // rethrow's immediate argument is always 0 (= the latest exception). + // CLEANUPRETs have the EH pad BB the exception to rethrow has been caught + // as an argument. Use it and change the instruction opcode to 'RETHROW' + // to make rethrowing instructions consistent. + // + // This is because we cannot safely assume that it is always the nearest + // dominating EH pad, in case there are code transformations such as + // inlining. BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) - .addImm(0); + .addMBB(TI->getOperand(0).getMBB()); TI->eraseFromParent(); Changed = true; break; diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index 5a8177e2b3607..9b13447754e4c 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -195,7 +195,8 @@ def EH_RETURN64 : I<0xC3, RawFrm, (outs), (ins GR64:$addr), let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, isCodeGenOnly = 1, isReturn = 1, isEHScopeReturn = 1 in { - def CLEANUPRET : I<0, Pseudo, (outs), (ins), "# CLEANUPRET", [(cleanupret)]>; + def CLEANUPRET : I<0, Pseudo, (outs), (ins), "# CLEANUPRET", + [(cleanupret bb)]>; // CATCHRET needs a custom inserter for SEH. let usesCustomInserter = 1 in diff --git a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp index 2ec5da4886839..2cf42597df13a 100644 --- a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp +++ b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp @@ -37,6 +37,9 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" +/* TO_UPSTREAM(BoundsSafety) ON*/ +#include "llvm/IR/Constants.h" +/* TO_UPSTREAM(BoundsSafety) OFF*/ #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/EHPersonalities.h" @@ -135,6 +138,27 @@ void analyzeProfMetadata(BasicBlock *BB, AnnotatedColdBlocks.insert(CondBr->getSuccessor(1)); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +bool isBoundsSafetyTrapCall(const CallBase *CB) { + const auto *CF = CB->getCalledFunction(); + if (!CF) + return false; + if (!CF->isIntrinsic()) + return false; + if (CF->getIntrinsicID() != Intrinsic::ubsantrap) + return false; + + assert(CB->arg_size() == 1); + const auto *Arg = CB->getArgOperand(0); + const auto *ConstArg = cast(Arg); + + auto ArgValue = ConstArg->getZExtValue(); + assert(ArgValue <= UINT8_MAX); + // FIXME: Don't hardcode this value + return ArgValue == 0x19; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + bool unlikelyExecuted(BasicBlock &BB) { // Exception handling blocks are unlikely executed. if (BB.isEHPad() || isa(BB.getTerminator())) @@ -145,7 +169,10 @@ bool unlikelyExecuted(BasicBlock &BB) { for (Instruction &I : BB) if (auto *CB = dyn_cast(&I)) if (CB->hasFnAttr(Attribute::Cold) && - !CB->getMetadata(LLVMContext::MD_nosanitize)) + !CB->getMetadata(LLVMContext::MD_nosanitize) && + /* TO_UPSTREAM(BoundsSafety) ON*/ + !isBoundsSafetyTrapCall(CB) + /* TO_UPSTREAM(BoundsSafety) OFF*/) return true; // The block is cold if it has an unreachable terminator, unless it's diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 2a247127579bf..03eaa784cfbef 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -68,15 +68,15 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final TargetLibraryInfo &TLI, TargetTransformInfo &TTI, DominatorTree &DT, OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI, - ProfileSummaryInfo *PSI, const DataLayout &DL, LoopInfo *LI) + ProfileSummaryInfo *PSI, const DataLayout &DL, LoopInfo *LI, + ReversePostOrderTraversal &RPOT) : InstCombiner(Worklist, Builder, MinimizeSize, AA, AC, TLI, TTI, DT, ORE, - BFI, BPI, PSI, DL, LI) {} + BFI, BPI, PSI, DL, LI, RPOT) {} virtual ~InstCombinerImpl() = default; /// Perform early cleanup and prepare the InstCombine worklist. - bool prepareWorklist(Function &F, - ReversePostOrderTraversal &RPOT); + bool prepareWorklist(Function &F); /// Run the combiner over the entire worklist until it is empty. /// diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 0fb8b639c97b9..12e7c8ef0b968 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1812,12 +1812,10 @@ Instruction *InstCombinerImpl::foldOpIntoPhi(Instruction &I, PHINode *PN) { if (cast(InVal)->getParent() == NonSimplifiedBB) return nullptr; - // If the incoming non-constant value is reachable from the phis block, - // we'll push the operation across a loop backedge. This could result in + // Do not push the operation across a loop backedge. This could result in // an infinite combine loop, and is generally non-profitable (especially // if the operation was originally outside the loop). - if (isPotentiallyReachable(PN->getParent(), NonSimplifiedBB, nullptr, &DT, - LI)) + if (isBackEdge(NonSimplifiedBB, PN->getParent())) return nullptr; } @@ -5235,8 +5233,7 @@ class AliasScopeTracker { /// them to the worklist (this significantly speeds up instcombine on code where /// many instructions are dead or constant). Additionally, if we find a branch /// whose condition is a known constant, we only visit the reachable successors. -bool InstCombinerImpl::prepareWorklist( - Function &F, ReversePostOrderTraversal &RPOT) { +bool InstCombinerImpl::prepareWorklist(Function &F) { bool MadeIRChange = false; SmallPtrSet LiveBlocks; SmallVector InstrsForInstructionWorklist; @@ -5375,6 +5372,18 @@ bool InstCombinerImpl::prepareWorklist( return MadeIRChange; } +void InstCombiner::computeBackEdges() { + // Collect backedges. + SmallPtrSet Visited; + for (BasicBlock *BB : RPOT) { + Visited.insert(BB); + for (BasicBlock *Succ : successors(BB)) + if (Visited.contains(Succ)) + BackEdges.insert({BB, Succ}); + } + ComputedBackEdges = true; +} + static bool combineInstructionsOverFunction( Function &F, InstructionWorklist &Worklist, AliasAnalysis *AA, AssumptionCache &AC, TargetLibraryInfo &TLI, TargetTransformInfo &TTI, @@ -5418,9 +5427,9 @@ static bool combineInstructionsOverFunction( << F.getName() << "\n"); InstCombinerImpl IC(Worklist, Builder, F.hasMinSize(), AA, AC, TLI, TTI, DT, - ORE, BFI, BPI, PSI, DL, LI); + ORE, BFI, BPI, PSI, DL, LI, RPOT); IC.MaxArraySizeForCombine = MaxArraySize; - bool MadeChangeInThisIteration = IC.prepareWorklist(F, RPOT); + bool MadeChangeInThisIteration = IC.prepareWorklist(F); MadeChangeInThisIteration |= IC.run(); if (!MadeChangeInThisIteration) break; diff --git a/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp index 5d9a7bca7efec..7f6ddfe4e258c 100644 --- a/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp +++ b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp @@ -11,11 +11,18 @@ //===----------------------------------------------------------------------===// #include "llvm/Transforms/Scalar/AnnotationRemarks.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/DebugLoc.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/MemoryOpRemark.h" using namespace llvm; @@ -24,6 +31,88 @@ using namespace llvm::ore; #define DEBUG_TYPE "annotation-remarks" #define REMARK_PASS DEBUG_TYPE +/* TO_UPSTREAM(BoundsSafety) ON */ +static void tryEmitBoundsSafetyRemark(ArrayRef Instructions, + OptimizationRemarkEmitter &ORE) { + SmallSetVector Annotations; + SmallSetVector MissedOptimizationInfo; + SmallVector Pretty; + for (auto *I : Instructions) { + if (!I->hasMetadata(LLVMContext::MD_annotation)) + continue; + bool HasBoundsSafety = false; + for (const MDOperand &Op : + I->getMetadata(LLVMContext::MD_annotation)->operands()) { + StringRef S; + StringRef ExtraInfo; + if (isa(Op.get())) + S = cast(Op.get())->getString(); + else { + auto *AnnotationTuple = cast(Op.get()); + S = cast(AnnotationTuple->getOperand(0).get())->getString(); + ExtraInfo = + cast(AnnotationTuple->getOperand(1).get())->getString(); + } + if (S.starts_with("bounds-safety")) { + Annotations.insert(S); + HasBoundsSafety = true; + } + if (!ExtraInfo.empty()) + MissedOptimizationInfo.insert(ExtraInfo); + } + + if (!HasBoundsSafety) + continue; + switch (I->getOpcode()) { + case Instruction::ICmp: { + std::string R = + std::string("cmp ") + + CmpInst::getPredicateName(cast(I)->getPredicate()).str(); + if (I->getOperand(1)->getName().starts_with("upper")) + R += " UPPER_BOUND"; + + Pretty.push_back(R); + break; + } + case Instruction::And: + Pretty.push_back("and"); + break; + case Instruction::Br: + if (cast(I)->isConditional()) + Pretty.push_back("cond branch"); + else + Pretty.push_back("uncond branch"); + break; + case Instruction::Call: { + auto II = dyn_cast(I); + if (II && II->getIntrinsicID() == Intrinsic::ubsantrap) + Pretty.push_back("trap"); + else + Pretty.push_back("call"); + break; + } + default: + Pretty.push_back("other"); + break; + } + Pretty.back() = Pretty.back() + " (LLVM IR '" + I->getOpcodeName() + "')"; + } + + if (!Pretty.empty()) { + auto ORA = OptimizationRemarkAnalysis(REMARK_PASS, "BoundsSafetyCheck", + Instructions[0]); + ORA << "Inserted " << NV("count", Pretty.size()) << " LLVM IR instruction" + << (Pretty.size() > 1 ? "s" : "") << "\n" + << "used for:\n" + << join(Annotations, ", ") << "\n\ninstructions:\n" + << join(Pretty, "\n"); + if (!MissedOptimizationInfo.empty()) + ORA << "Missed Optimization Info\n" << join(MissedOptimizationInfo, "\n"); + ORE.emit(ORA); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + static void tryEmitAutoInitRemark(ArrayRef Instructions, OptimizationRemarkEmitter &ORE, const TargetLibraryInfo &TLI) { @@ -44,11 +133,14 @@ static void runImpl(Function &F, const TargetLibraryInfo &TLI) { return; // Track all annotated instructions aggregated based on their debug location. - DenseMap> DebugLoc2Annotated; + MapVector> DebugLoc2Annotated; OptimizationRemarkEmitter ORE(&F); // First, generate a summary of the annotated instructions. MapVector Mapping; +/* TO_UPSTREAM(BoundsSafety) ON */ + unsigned BoundsSafetySummaryCount = 0; +/* TO_UPSTREAM(BoundsSafety) OFF */ for (Instruction &I : instructions(F)) { if (!I.hasMetadata(LLVMContext::MD_annotation)) continue; @@ -65,6 +157,24 @@ static void runImpl(Function &F, const TargetLibraryInfo &TLI) { auto Iter = Mapping.insert({AnnotationStr, 0}); Iter.first->second++; } + /* TO_UPSTREAM(BoundsSafety) ON */ + // Compute the number of instructions with -fbounds-safety annotation. + if (any_of(I.getMetadata(LLVMContext::MD_annotation)->operands(), + [](const MDOperand &Op) { + // skip bounds-safety-missed remarks. + if (!isa(Op.get())) { + auto *AnnotationTuple = cast(Op.get()); + auto Str = + cast(AnnotationTuple->getOperand(0).get()) + ->getString(); + if (Str.starts_with("bounds-safety-missed")) + return false; + } + auto AnnotationStr = cast(Op.get())->getString(); + return AnnotationStr.starts_with("bounds-safety"); + })) + BoundsSafetySummaryCount++; + /* TO_UPSTREAM(BoundsSafety) OFF */ } for (const auto &KV : Mapping) @@ -73,13 +183,25 @@ static void runImpl(Function &F, const TargetLibraryInfo &TLI) { << "Annotated " << NV("count", KV.second) << " instructions with " << NV("type", KV.first)); - // For each debug location, look for all the instructions with annotations and - // generate more detailed remarks to be displayed at that location. + /* TO_UPSTREAM(BoundsSafety) ON */ + if (BoundsSafetySummaryCount > 0) + ORE.emit(OptimizationRemarkAnalysis(REMARK_PASS, "AnnotationSummary", + F.getSubprogram(), &F.front()) + << "Annotated " << NV("count", BoundsSafetySummaryCount) + << " instructions with " << NV("type", "bounds-safety-total-summary")); + /* TO_UPSTREAM(BoundsSafety) OFF */ + + // For each debug location, look for all the instructions with + // annotations and generate more detailed remarks to be displayed at + // that location. for (auto &KV : DebugLoc2Annotated) { // Don't generate remarks with no debug location. if (!KV.first) continue; + /* TO_UPSTREAM(BoundsSafety) ON */ + tryEmitBoundsSafetyRemark(KV.second, ORE); + /* TO_UPSTREAM(BoundsSafety) OFF */ tryEmitAutoInitRemark(KV.second, ORE, TLI); } } diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index ba09ebf8b04c4..c883d65f7ef57 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -14,6 +14,7 @@ add_llvm_component_library(LLVMScalarOpts EarlyCSE.cpp FlattenCFGPass.cpp Float2Int.cpp + LoopTrapAnalysis.cpp GuardWidening.cpp GVN.cpp GVNHoist.cpp diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index f81430ba4d938..7b3408c434eb2 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -14,15 +14,18 @@ #include "llvm/Transforms/Scalar/ConstraintElimination.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/ConstraintSystem.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/IVDescriptors.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -33,6 +36,7 @@ #include "llvm/IR/PatternMatch.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" +#include "llvm/Remarks/BoundsSafetyOptRemarks.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/DebugCounter.h" @@ -180,18 +184,44 @@ struct FactOrCheck { bool isConditionFact() const { return Ty == EntryTy::ConditionFact; } }; +/// PHI pointer induction representation as a base pointer value, an integer +/// induction PHI variable as an offset and a scale value for decomposition. +struct InductionInfo { + Value *BasePtr; + PHINode *Index; + APInt Scale; +}; + /// Keep state required to build worklist. struct State { DominatorTree &DT; LoopInfo &LI; ScalarEvolution &SE; - SmallVector WorkList; - - State(DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE) - : DT(DT), LI(LI), SE(SE) {} + const DataLayout &DL; + LLVMContext &Ctx; + SmallVector ExtraCmps; - /// Process block \p BB and add known facts to work-list. - void addInfoFor(BasicBlock &BB); + SmallVector WorkList; + /// Map containing a value representing a maximum value which is known to not + /// overflow a GEP. Any index >= 0 and <= the known safe upper bound is known + /// to not overflow. Note that the known safe bound is an artificial IR value + /// and known facts are added as conditions restricting the bound. + DenseMap PtrKnownSafeBound; + /// Map containing the pointer induction PHIs and their representation using + /// the PHI initial value as a base and an integer as an offset if it is in + /// the same loop, has the same step value as the pointer induction variable + /// and starts at 0 and a scale obtained from the step value of the pointer + /// induction. + DenseMap PtrInductionPHIInfoMap; + + State(DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE, + const DataLayout &DL, LLVMContext &Ctx) + : DT(DT), LI(LI), SE(SE), DL(DL), Ctx(Ctx) {} + + ~State() { + for (Instruction *I : reverse(ExtraCmps)) + I->deleteValue(); + } /// Try to add facts for loop inductions (AddRecs) in EQ/NE compares /// controlling the loop header. @@ -202,6 +232,39 @@ struct State { bool canAddSuccessor(BasicBlock &BB, BasicBlock *Succ) const { return DT.dominates(BasicBlockEdge(&BB, Succ), Succ); } + + // Collect all induction PHIs for all loops in the function and represent a + // pointer induction phi with its initial value as a base pointer and an + // offset if we find a suitable integer induction phi in the same loop and + // same increment value as the pointer phi. + void collectPHIInductionVars(Function &F); + + /// Process block \p BB and add known facts to work-list. + void addInfoFor(BasicBlock &BB); + void addPointerBoundInfo(GEPOperator *GEP, DomTreeNode *DTN); + void addPointerBoundInfo(Value *PtrOp, Value *IdxOp, DomTreeNode *DTN, + Constant *Scale); + /// Try to add a known-safe upper bound from \p Op if \p Op is a condition + /// checking whether a GEP overflows. \p Op must be true in \p DTN. + void addPointerBoundInfoFromOverflowCheck(Value *Op, DomTreeNode *DTN); + + Value *zeroExtendIfNeeded(Value *Op, Type *ToTy) { + if (Op->getType()->getScalarSizeInBits() < ToTy->getScalarSizeInBits()) { + Value *Inner; + if (match(Op, m_ZExt(m_Value(Inner)))) + Op = Inner; + Op = new ZExtInst(Op, ToTy); + ExtraCmps.push_back(cast(Op)); + } + return Op; + } + + /// \returns a expression to compute the integer offset for a GEP with + /// constant offset \p ConstantOffset and \p VariableOffsets for inbounds + /// precondition checking. + Value * + createIndexExpression(APInt ConstantOffset, + const MapVector &VariableOffsets); }; class ConstraintInfo; @@ -270,11 +333,11 @@ class ConstraintInfo { ConstraintSystem UnsignedCS; ConstraintSystem SignedCS; - const DataLayout &DL; + struct State &State; public: - ConstraintInfo(const DataLayout &DL, ArrayRef FunctionArgs) - : UnsignedCS(FunctionArgs), SignedCS(FunctionArgs), DL(DL) { + ConstraintInfo(struct State &State, ArrayRef FunctionArgs) + : UnsignedCS(FunctionArgs), SignedCS(FunctionArgs), State(State) { auto &Value2Index = getValue2Index(false); // Add Arg > -1 constraints to unsigned system for all function arguments. for (Value *Arg : FunctionArgs) { @@ -314,7 +377,8 @@ class ConstraintInfo { /// New variables that need to be added to the system are collected in /// \p NewVariables. ConstraintTy getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1, - SmallVectorImpl &NewVariables) const; + SmallVectorImpl &NewVariables, + bool ShouldDecompose) const; /// Turns a comparison of the form \p Op0 \p Pred \p Op1 into a vector of /// constraints using getConstraint. Returns an empty constraint if the result @@ -324,7 +388,7 @@ class ConstraintInfo { /// which increases the effectiveness of the signed <-> unsigned transfer /// logic. ConstraintTy getConstraintForSolving(CmpInst::Predicate Pred, Value *Op0, - Value *Op1) const; + Value *Op1, bool ShouldDecompose) const; /// Try to add information from \p A \p Pred \p B to the unsigned/signed /// system if \p Pred is signed/unsigned. @@ -432,37 +496,52 @@ static OffsetResult collectOffsets(GEPOperator &GEP, const DataLayout &DL) { static Decomposition decompose(Value *V, SmallVectorImpl &Preconditions, - bool IsSigned, const DataLayout &DL); + bool IsSigned, State &State); static bool canUseSExt(ConstantInt *CI) { const APInt &Val = CI->getValue(); return Val.sgt(MinSignedConstraintValue) && Val.slt(MaxConstraintValue); } -static Decomposition decomposeGEP(GEPOperator &GEP, - SmallVectorImpl &Preconditions, - bool IsSigned, const DataLayout &DL) { +static Decomposition +decomposeGEP(GEPOperator &GEP, SmallVectorImpl &Preconditions, + bool IsSigned, State &State) { // Do not reason about pointers where the index size is larger than 64 bits, // as the coefficients used to encode constraints are 64 bit integers. - if (DL.getIndexTypeSizeInBits(GEP.getPointerOperand()->getType()) > 64) + if (State.DL.getIndexTypeSizeInBits(GEP.getPointerOperand()->getType()) > 64) return &GEP; assert(!IsSigned && "The logic below only supports decomposition for " "unsigned predicates at the moment."); const auto &[BasePtr, ConstantOffset, VariableOffsets, AllInbounds] = - collectOffsets(GEP, DL); - if (!BasePtr || !AllInbounds) + collectOffsets(GEP, State.DL); + if (!BasePtr) return &GEP; + if (!AllInbounds) { + // If a GEP is not inbounds, check if we have a known-safe bounds. + // If we have, add a precondition to make sure the GEP index is <= + // the known-safe bound. + auto Iter = State.PtrKnownSafeBound.find(BasePtr); + if (Iter != State.PtrKnownSafeBound.end()) { + Value *IndexExpr = + State.createIndexExpression(ConstantOffset, VariableOffsets); + if (!IndexExpr) + return &GEP; + Preconditions.emplace_back(CmpInst::ICMP_ULE, IndexExpr, Iter->second); + } else + return &GEP; + } + Decomposition Result(ConstantOffset.getSExtValue(), DecompEntry(1, BasePtr)); for (auto [Index, Scale] : VariableOffsets) { - auto IdxResult = decompose(Index, Preconditions, IsSigned, DL); + auto IdxResult = decompose(Index, Preconditions, IsSigned, State); IdxResult.mul(Scale.getSExtValue()); Result.add(IdxResult); // If Op0 is signed non-negative, the GEP is increasing monotonically and // can be de-composed. - if (!isKnownNonNegative(Index, DL)) + if (!isKnownNonNegative(Index, State.DL)) Preconditions.emplace_back(CmpInst::ICMP_SGE, Index, ConstantInt::get(Index->getType(), 0)); } @@ -474,20 +553,41 @@ static Decomposition decomposeGEP(GEPOperator &GEP, // pairs equals \p V. static Decomposition decompose(Value *V, SmallVectorImpl &Preconditions, - bool IsSigned, const DataLayout &DL) { - - auto MergeResults = [&Preconditions, IsSigned, &DL](Value *A, Value *B, - bool IsSignedB) { - auto ResA = decompose(A, Preconditions, IsSigned, DL); - auto ResB = decompose(B, Preconditions, IsSignedB, DL); + bool IsSigned, State &State) { + auto DL = State.DL; + auto MergeResults = [&Preconditions, IsSigned, &State]( + Value *A, Value *B, bool IsSignedB) -> Decomposition { + auto ResA = decompose(A, Preconditions, IsSigned, State); + auto ResB = decompose(B, Preconditions, IsSignedB, State); ResA.add(ResB); return ResA; }; Type *Ty = V->getType()->getScalarType(); if (Ty->isPointerTy() && !IsSigned) { + // If PHI node is found to be represented as a base ptr + offset + // where offset is coresponding to an integer induction PHI instruction + // it can be represented as base + decomp(offset) * scale. + if (auto *PHI = dyn_cast(V)) { + auto It = State.PtrInductionPHIInfoMap.find(PHI); + if (It == State.PtrInductionPHIInfoMap.end()) + return {V, false}; + + assert(PHI->getNumIncomingValues() == 2 && + "Only PHI with two operands is supported"); + Value *BasePtr = It->getSecond().BasePtr; + Value *Index = It->getSecond().Index; + int64_t Scale = It->getSecond().Scale.getSExtValue(); + Decomposition PHIResult(BasePtr); + Decomposition IdxResult = + decompose(Index, Preconditions, IsSigned, State); + IdxResult.mul(Scale); + PHIResult.add(IdxResult); + return PHIResult; + } + if (auto *GEP = dyn_cast(V)) - return decomposeGEP(*GEP, Preconditions, IsSigned, DL); + return decomposeGEP(*GEP, Preconditions, IsSigned, State); if (isa(V)) return int64_t(0); @@ -523,7 +623,7 @@ static Decomposition decompose(Value *V, ConstantInt *CI; if (match(V, m_NSWMul(m_Value(Op0), m_ConstantInt(CI))) && canUseSExt(CI)) { - auto Result = decompose(Op0, Preconditions, IsSigned, DL); + auto Result = decompose(Op0, Preconditions, IsSigned, State); Result.mul(CI->getSExtValue()); return Result; } @@ -534,7 +634,7 @@ static Decomposition decompose(Value *V, uint64_t Shift = CI->getValue().getLimitedValue(); if (Shift < Ty->getIntegerBitWidth() - 1) { assert(Shift < 64 && "Would overflow"); - auto Result = decompose(Op0, Preconditions, IsSigned, DL); + auto Result = decompose(Op0, Preconditions, IsSigned, State); Result.mul(int64_t(1) << Shift); return Result; } @@ -550,7 +650,7 @@ static Decomposition decompose(Value *V, } Value *Op0; - if (match(V, m_ZExt(m_Value(Op0)))) { + while (match(V, m_ZExt(m_Value(Op0)))) { IsKnownNonNegative = true; V = Op0; } @@ -592,21 +692,21 @@ static Decomposition decompose(Value *V, if (match(V, m_NUWShl(m_Value(Op1), m_ConstantInt(CI))) && canUseSExt(CI)) { if (CI->getSExtValue() < 0 || CI->getSExtValue() >= 64) return {V, IsKnownNonNegative}; - auto Result = decompose(Op1, Preconditions, IsSigned, DL); + auto Result = decompose(Op1, Preconditions, IsSigned, State); Result.mul(int64_t{1} << CI->getSExtValue()); return Result; } if (match(V, m_NUWMul(m_Value(Op1), m_ConstantInt(CI))) && canUseSExt(CI) && (!CI->isNegative())) { - auto Result = decompose(Op1, Preconditions, IsSigned, DL); + auto Result = decompose(Op1, Preconditions, IsSigned, State); Result.mul(CI->getSExtValue()); return Result; } if (match(V, m_NUWSub(m_Value(Op0), m_Value(Op1)))) { - auto ResA = decompose(Op0, Preconditions, IsSigned, DL); - auto ResB = decompose(Op1, Preconditions, IsSigned, DL); + auto ResA = decompose(Op0, Preconditions, IsSigned, State); + auto ResB = decompose(Op1, Preconditions, IsSigned, State); ResA.sub(ResB); return ResA; } @@ -616,7 +716,8 @@ static Decomposition decompose(Value *V, ConstraintTy ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1, - SmallVectorImpl &NewVariables) const { + SmallVectorImpl &NewVariables, + bool ShouldDecompose) const { assert(NewVariables.empty() && "NewVariables must be empty when passed in"); bool IsEq = false; bool IsNe = false; @@ -656,13 +757,18 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1, Pred != CmpInst::ICMP_SLE && Pred != CmpInst::ICMP_SLT) return {}; - SmallVector Preconditions; bool IsSigned = CmpInst::isSigned(Pred); auto &Value2Index = getValue2Index(IsSigned); - auto ADec = decompose(Op0->stripPointerCastsSameRepresentation(), - Preconditions, IsSigned, DL); - auto BDec = decompose(Op1->stripPointerCastsSameRepresentation(), - Preconditions, IsSigned, DL); + SmallVector Preconditions; + Decomposition ADec = Op0->stripPointerCastsSameRepresentation(); + Decomposition BDec = Op1->stripPointerCastsSameRepresentation(); + if (ShouldDecompose) { + ADec = decompose(Op0->stripPointerCastsSameRepresentation(), Preconditions, + IsSigned, State); + BDec = decompose(Op1->stripPointerCastsSameRepresentation(), Preconditions, + IsSigned, State); + } + int64_t Offset1 = ADec.Offset; int64_t Offset2 = BDec.Offset; Offset1 *= -1; @@ -746,9 +852,10 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1, return Res; } -ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred, - Value *Op0, - Value *Op1) const { +ConstraintTy +ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred, Value *Op0, + Value *Op1, + bool ShouldDecompose) const { Constant *NullC = Constant::getNullValue(Op0->getType()); // Handle trivially true compares directly to avoid adding V UGE 0 constraints // for all variables in the unsigned system. @@ -760,6 +867,7 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred, false, false); } + auto DL = State.DL; // If both operands are known to be non-negative, change signed predicates to // unsigned ones. This increases the reasoning effectiveness in combination // with the signed <-> unsigned transfer logic. @@ -769,7 +877,7 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred, Pred = CmpInst::getUnsignedPredicate(Pred); SmallVector NewVariables; - ConstraintTy R = getConstraint(Pred, Op0, Op1, NewVariables); + ConstraintTy R = getConstraint(Pred, Op0, Op1, NewVariables, ShouldDecompose); if (!NewVariables.empty()) return {}; return R; @@ -828,7 +936,7 @@ ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const { bool ConstraintInfo::doesHold(CmpInst::Predicate Pred, Value *A, Value *B) const { - auto R = getConstraintForSolving(Pred, A, B); + auto R = getConstraintForSolving(Pred, A, B, true); return R.isValid(*this) && getCS(R.IsSigned).isConditionImplied(R.Coefficients); } @@ -838,7 +946,7 @@ void ConstraintInfo::transferToOtherSystem( unsigned NumOut, SmallVectorImpl &DFSInStack) { auto IsKnownNonNegative = [this](Value *V) { return doesHold(CmpInst::ICMP_SGE, V, ConstantInt::get(V->getType(), 0)) || - isKnownNonNegative(V, DL, /*Depth=*/MaxAnalysisRecursionDepth - 1); + isKnownNonNegative(V, State.DL, /*Depth=*/MaxAnalysisRecursionDepth - 1); }; // Check if we can combine facts from the signed and unsigned systems to // derive additional facts. @@ -890,8 +998,33 @@ void ConstraintInfo::transferToOtherSystem( } } -#ifndef NDEBUG +// Returns a pair {Value, NeedZExt}, if \p Exp is either a SCEVConstant or a +// SCEVUnknown. If the value needs to be zero extended, NeedZExt is true. +static std::pair getValueOrConstant(const SCEV *Exp, Loop *L, + ScalarEvolution &SE) { + bool NeedZExt = false; + if (auto *Add = dyn_cast(Exp)) { + auto *C = dyn_cast(Add->getOperand(0)); + auto *AddWithGuardInfo = SE.applyLoopGuards(Add->getOperand(1), L); + if (C && C->getValue()->getSExtValue() == -1 && + SE.isKnownPositive(AddWithGuardInfo)) { + Exp = Add->getOperand(1); + } + } + + if (isa(Exp)) { + Exp = cast(Exp)->getOperand(0); + NeedZExt = true; + } + Value *V = nullptr; + if (auto *U = dyn_cast(Exp)) + V = U->getValue(); + else if (auto *C = dyn_cast(Exp)) + V = C->getValue(); + return {V, NeedZExt}; +} +#ifndef NDEBUG static void dumpConstraint(ArrayRef C, const DenseMap &Value2Index) { ConstraintSystem CS(Value2Index); @@ -900,6 +1033,200 @@ static void dumpConstraint(ArrayRef C, } #endif +Value * +State::createIndexExpression(APInt ConstantOffset, + const MapVector &VariableOffsets) { + unsigned BitWidth = ConstantOffset.getBitWidth(); + + Type *IntTy = Type::getIntNTy(Ctx, BitWidth); + Value *ConstOffsetValue = ConstantInt::get(IntTy, ConstantOffset); + // TODO: support multiple variable offsets. + if (VariableOffsets.size() > 1) + return nullptr; + if (VariableOffsets.size() == 0) + return ConstOffsetValue; + if (VariableOffsets.begin()->first->getType()->getScalarSizeInBits() > + BitWidth) + return nullptr; + + // Compute offset as VariableOffset * Scale + ConstantOffset. + Value *IdxOp = zeroExtendIfNeeded(VariableOffsets.begin()->first, IntTy); + + Value *Scale = + ConstantInt::get(IntTy, VariableOffsets.begin()->second.getSExtValue()); + Value *UpperBound = BinaryOperator::CreateNUW(Instruction::Mul, IdxOp, Scale); + cast(UpperBound)->setHasNoSignedWrap(); + ExtraCmps.push_back(cast(UpperBound)); + if (!ConstantOffset.isZero()) { + UpperBound = BinaryOperator::CreateNUW(Instruction::Add, UpperBound, + ConstOffsetValue); + cast(UpperBound)->setHasNoSignedWrap(); + if (auto *I = dyn_cast(UpperBound)) + ExtraCmps.push_back(I); + } + return UpperBound; +} + +void State::addPointerBoundInfo(GEPOperator *GEP, DomTreeNode *DTN) { + const auto &[BasePtr, ConstantOffset, VariableOffsets, AllInbounds] = + collectOffsets(*GEP, DL); + if (!BasePtr) + return; + Value *UpperBound = createIndexExpression(ConstantOffset, VariableOffsets); + if (UpperBound) + addPointerBoundInfo(BasePtr, UpperBound, DTN, nullptr); +} + +void State::addPointerBoundInfo(Value *PtrOp, Value *IdxOp, DomTreeNode *DTN, + Constant *Scale) { + ConditionTy HoldsIf; + if (!isKnownNonNegative(IdxOp, DL, 2)) { + HoldsIf = ConditionTy(CmpInst::ICMP_SGE, IdxOp, + ConstantInt::get(IdxOp->getType(), 0)); + } + if (Scale) { + auto *Mul = BinaryOperator::CreateNUW(Instruction::Mul, IdxOp, Scale); + ExtraCmps.push_back(Mul); + IdxOp = Mul; + } + + // We know that IdxOp does not cause an overflow. Now try to add a fact + // for UB >= IdxOp. + auto I = PtrKnownSafeBound.insert({PtrOp, nullptr}); + if (I.second) { + // Create a dummy instruction we can use to compare the bounds + // against. + I.first->second = + new LoadInst(IdxOp->getType(), + UndefValue::get(PointerType::get(IdxOp->getType(), 0)), + "ub", false, Align(1)); + ExtraCmps.push_back(cast(I.first->second)); + } + if (I.first->second->getType() == IdxOp->getType()) { + WorkList.push_back(FactOrCheck::getConditionFact( + DTN, CmpInst::ICMP_UGE, I.first->second, IdxOp, HoldsIf)); + } +} + +/* TO_UPSTREAM(BoundsSafety) ON*/ +// Annotate PHI ptr induction step to indicate missed +// optimization opportunities. The annotations will be later emitted as part of +// AnnotationRemarks pass. +static void annotateMissedPhiOptimisation(Instruction *PtrPHI, int64_t PHIStep, + int64_t IntPHIStep) { + if (abs(PHIStep) == abs(IntPHIStep)) { + annotateRuntimeChecks( + PtrPHI, BoundsSafetyOptRemarkKind::BNS_MISSED_REMARK_PHI_DIRECTION); + } else if (PHIStep != IntPHIStep) + annotateRuntimeChecks( + PtrPHI, BoundsSafetyOptRemarkKind::BNS_MISSED_REMARK_PHI_STEP_SIZE); +} + +/// Recurse through \p I until we find a binary operator with no nsw/nuw flags +/// and annotate it with bounds-safety-missed-optimization-nuw and +/// bounds-safety-missed-optimization-nsw. +void annotateMissedReplaceConditions(Instruction *I, bool IsSigned, int Depth) { + if (Depth == 0) + return; + for (auto &Op : I->operands()) { + if (!isa(Op)) + continue; + auto *OpInst = cast(Op); + if (isa(OpInst)) { + if (IsSigned && !OpInst->hasNoSignedWrap()) { + annotate(OpInst, + BoundsSafetyOptRemarkKind::BNS_MISSED_REMARK_MISS_NSW_FLAG); + return; + } + if (!OpInst->hasNoUnsignedWrap()) { + annotate(OpInst, + BoundsSafetyOptRemarkKind::BNS_MISSED_REMARK_MISS_NUW_FLAG); + return; + } + } + annotateMissedReplaceConditions(OpInst, IsSigned, Depth - 1); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +void State::collectPHIInductionVars(Function &F) { + for (auto *Loop : LI.getLoopsInPreorder()) { + if (!Loop->getLoopPreheader()) + continue; + SmallVector> IntPHIIndDescPairs; + SmallVector> PtrPHIIndDescPairs; + for (PHINode &PHI : Loop->getHeader()->phis()) { + if (PHI.getNumIncomingValues() != 2) + continue; + InductionDescriptor IndDesc; + if (!InductionDescriptor::isInductionPHI(&PHI, Loop, &SE, IndDesc)) + continue; + if (!IndDesc.getConstIntStepValue()) + continue; + if (PHI.getType()->isIntegerTy()) { + IntPHIIndDescPairs.emplace_back(&PHI, IndDesc); + } else if (PHI.getType()->isPointerTy()) { + auto *InductionStep = dyn_cast( + PHI.getIncomingValueForBlock(Loop->getLoopLatch())); + if (!InductionStep || InductionStep->getNumOperands() != 2) + continue; + PtrPHIIndDescPairs.emplace_back(&PHI, IndDesc); + } + } + for (auto PtrPHIIndDescPair : PtrPHIIndDescPairs) { + PHINode *PtrPHI = PtrPHIIndDescPair.first; + InductionDescriptor PtrIndDesc = PtrPHIIndDescPair.second; + Value *InitialPointer = PtrIndDesc.getStartValue(); + auto *InductionStep = cast( + PtrPHI->getIncomingValueForBlock(Loop->getLoopLatch())); + for (auto IntPHIIndDescPair : IntPHIIndDescPairs) { + PHINode *IntPHI = IntPHIIndDescPair.first; + InductionDescriptor IntPHIIndDesc = IntPHIIndDescPair.second; + int64_t IntPHIStep = + IntPHIIndDesc.getConstIntStepValue()->getSExtValue(); + int64_t PHIStep = PtrIndDesc.getConstIntStepValue()->getSExtValue(); + int64_t PHIStepSize = + DL.getTypeAllocSize(InductionStep->getResultElementType()); + PHIStep /= PHIStepSize; + if (IntPHIStep == PHIStep && + isa(IntPHIIndDesc.getStartValue()) && + cast(IntPHIIndDesc.getStartValue())->getSExtValue() == + 0) { + LLVM_DEBUG(dbgs() << "Represent " << PtrPHI->getNameOrAsOperand() + << " as \n " << PtrPHI->getNameOrAsOperand() + << " = " << InitialPointer->getNameOrAsOperand() + << " + " << IntPHI->getNameOrAsOperand() << "\n"); + auto BitWidth = DL.getIndexTypeSizeInBits(InitialPointer->getType()); + auto Scale = + APInt(BitWidth, + DL.getTypeAllocSize(InductionStep->getResultElementType())); + PtrInductionPHIInfoMap.insert( + {PtrPHI, {InitialPointer, IntPHI, Scale}}); + } + /* TO_UPSTREAM(BoundsSafety) ON */ + else + annotateMissedPhiOptimisation(PtrPHI, PHIStep, IntPHIStep); + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + } + } +} + +void State::addPointerBoundInfoFromOverflowCheck(Value *Op, DomTreeNode *DTN) { + CmpInst::Predicate Pred; + Value *Ptr1; + Value *G; + if (match(Op, m_Cmp(Pred, m_Value(G), m_Value(Ptr1))) && + Pred == CmpInst::ICMP_UGE) { + G = G->stripPointerCastsSameRepresentation(); + Ptr1 = Ptr1->stripPointerCastsSameRepresentation(); + if (auto *GEP = dyn_cast(G)) { + if (GEP->getPointerOperand() == Ptr1) + addPointerBoundInfo(cast(GEP), DTN); + } + } +} + void State::addInfoForInductions(BasicBlock &BB) { auto *L = LI.getLoopFor(&BB); if (!L || L->getHeader() != &BB) @@ -1056,6 +1383,20 @@ void State::addInfoFor(BasicBlock &BB) { // Queue conditions and assumes. for (Instruction &I : BB) { if (auto Cmp = dyn_cast(&I)) { + for (Value *Op : Cmp->operands()) { + if (!isa(Op)) + continue; + auto *GEP = dyn_cast(Op); + if (!GEP || !GEP->isInBounds()) + continue; + + SmallSet KnownPoison; + KnownPoison.insert(GEP); + + if (GuaranteedToExecute && programUndefinedIfPoison(Cmp)) + addPointerBoundInfo(GEP, DT.getNode(&BB)); + } + for (Use &U : Cmp->uses()) { auto *UserI = getContextInstForUse(U); auto *DTN = DT.getNode(UserI->getParent()); @@ -1110,6 +1451,51 @@ void State::addInfoFor(BasicBlock &BB) { break; } + // Try to derive known-safe non-overflowing bounds for pointers based on the + // instructions in the block. + if (auto *GEP = dyn_cast(&I)) { + // If the GEP is inbounds, we know that it cannot overflow (and it will + // less than its base), if the program is undefined if the GEP is + // poison. + + if (GEP->isInBounds()) { + if (programUndefinedIfPoison(GEP)) { + addPointerBoundInfo(cast(GEP), DT.getNode(&BB)); + } else { + BasicBlock *LastBB = nullptr; + // If the program is not UB directly if GEP is poison, individual + // uses of GEP may trigger UB if it is poison. In that case, the + // upper bound fact can be added to the block containing the user, + // *if* the user executes unconditionally. + for (auto &U : GEP->uses()) { + Instruction *User = cast(U.getUser()); + BasicBlock *UserBB = User->getParent(); + if (!DT.isReachableFromEntry(UserBB)) + continue; + + // If the upper bound fact already has been added to LastBB the + // current user can be skipped, if LastBB dominates the user's + // block. + if (LastBB && (LastBB == UserBB || DT.dominates(LastBB, UserBB))) + continue; + SmallSet KnownPoison; + KnownPoison.insert(GEP); + // If GEP is poison the program is UB if either: + // 1. U propagates poison and the program is UB if User is poison + // 2. User directly triggers UB if GEP is poison. + bool TriggersUB = + (propagatesPoison(U) && programUndefinedIfPoison(User)) || + mustTriggerUB(User, KnownPoison); + if (!TriggersUB || !isGuaranteedToTransferExecutionToSuccessor( + UserBB->begin(), User->getIterator())) + continue; + LastBB = UserBB; + addPointerBoundInfo(cast(GEP), DT.getNode(UserBB)); + } + } + } + } + GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I); } @@ -1129,6 +1515,67 @@ void State::addInfoFor(BasicBlock &BB) { if (!Br || !Br->isConditional()) return; + auto *L = LI.getLoopFor(&BB); + if (L && &BB == L->getHeader()) { + const SCEV *BTC = SE.getSymbolicMaxBackedgeTakenCount(L); + for (PHINode &PN : BB.phis()) { + if (!SE.isSCEVable(PN.getType())) + continue; + auto *IV = dyn_cast(SE.getSCEV(&PN)); + if (!IV) + continue; + + auto *C = dyn_cast(IV->getStart()); + // Make sure we have an induction starting at 0, stepping by 1 and not + // wrapping in the unsigned sense. Otherwise, evaluateAtIteration may + // return a result that wraps, which can lead to inconsistent constraints + // being added. + if (!C || !C->isZero() || !IV->getStepRecurrence(SE)->isOne()) + continue; + + // Evaluate IV at BTC, if there's either an exact BTC (in which case + // evaluating at the last iteration cannot wrap) or if there's a symbolic + // max BTC that's not constant. If we evaluate at a constant maximum, we + // are guaranteed to wrap. + if (IV->hasNoUnsignedWrap() && (!isa(SE.getBackedgeTakenCount(L)) || + (!isa(BTC) && !isa(BTC)))) { + auto *IVAtEnd = IV->evaluateAtIteration(BTC, SE); + + bool NeedZExt = false; + Value *V; + std::tie(V, NeedZExt) = getValueOrConstant(IVAtEnd, L, SE); + auto Monotonic = SE.getMonotonicPredicateType(IV, CmpInst::ICMP_UGE); + bool CanBuildCmp = + V && (!PN.getType()->isPointerTy() || V->getType() == PN.getType()); + if (CanBuildCmp && Monotonic && + Monotonic == ScalarEvolution::MonotonicallyIncreasing) { + if (NeedZExt) { + V = new ZExtInst(V, PN.getType()); + ExtraCmps.push_back(cast(V)); + } + WorkList.push_back(FactOrCheck::getConditionFact( + DT.getNode(&BB), CmpInst::ICMP_ULE, &PN, V)); + } + } + + auto *StartV = IV->getStart(); + bool NeedZExt; + Value *V; + std::tie(V, NeedZExt) = getValueOrConstant(StartV, L, SE); + bool CanBuildCmp = + V && !NeedZExt && + (!PN.getType()->isPointerTy() || V->getType() == PN.getType()); + if (CanBuildCmp && SE.isKnownPredicate(CmpInst::ICMP_SGE, IV, StartV)) { + WorkList.push_back(FactOrCheck::getConditionFact( + DT.getNode(&BB), CmpInst::ICMP_SGE, &PN, V)); + } + if (CanBuildCmp && SE.isKnownPredicate(CmpInst::ICMP_UGE, IV, StartV)) { + WorkList.push_back(FactOrCheck::getConditionFact( + DT.getNode(&BB), CmpInst::ICMP_UGE, &PN, V)); + } + } + } + Value *Cond = Br->getCondition(); // If the condition is a chain of ORs/AND and the successor only has the @@ -1151,6 +1598,28 @@ void State::addInfoFor(BasicBlock &BB) { if (SeenCond.insert(V).second) CondWorkList.push_back(V); }; + if (IsAnd) { + // For optimal results, pointer bounds need to be added before adding + // any constraints for the chain. + QueueValue(Op1); + QueueValue(Op0); + while (!CondWorkList.empty()) { + Value *Cur = CondWorkList.pop_back_val(); + if (auto *Cmp = dyn_cast(Cur)) { + addPointerBoundInfoFromOverflowCheck(Cmp, DT.getNode(Successor)); + continue; + } + Value *Op0, *Op1; + if (match(Cur, m_LogicalAnd(m_Value(Op0), m_Value(Op1)))) { + QueueValue(Op1); + QueueValue(Op0); + continue; + } + } + CondWorkList.clear(); + SeenCond.clear(); + } + QueueValue(Op1); QueueValue(Op0); while (!CondWorkList.empty()) { @@ -1181,10 +1650,12 @@ void State::addInfoFor(BasicBlock &BB) { auto *CmpI = dyn_cast(Br->getCondition()); if (!CmpI) return; - if (canAddSuccessor(BB, Br->getSuccessor(0))) + if (canAddSuccessor(BB, Br->getSuccessor(0))) { + addPointerBoundInfoFromOverflowCheck(CmpI, DT.getNode(Br->getSuccessor(0))); WorkList.emplace_back(FactOrCheck::getConditionFact( DT.getNode(Br->getSuccessor(0)), CmpI->getPredicate(), CmpI->getOperand(0), CmpI->getOperand(1))); + } if (canAddSuccessor(BB, Br->getSuccessor(1))) WorkList.emplace_back(FactOrCheck::getConditionFact( DT.getNode(Br->getSuccessor(1)), @@ -1357,10 +1828,22 @@ static std::optional checkCondition(CmpInst::Predicate Pred, Value *A, ConstraintInfo &Info) { LLVM_DEBUG(dbgs() << "Checking " << *CheckInst << "\n"); - auto R = Info.getConstraintForSolving(Pred, A, B); - if (R.empty() || !R.isValid(Info)){ - LLVM_DEBUG(dbgs() << " failed to decompose condition\n"); - return std::nullopt; + // If the constraint has a pre-condition, skip the constraint if it does not + // hold. + auto R = Info.getConstraintForSolving(Pred, A, B, true); + if (R.empty() || !R.isValid(Info)) { + R = Info.getConstraintForSolving(Pred, A, B, false); + if (R.empty() || !R.isValid(Info)) { + LLVM_DEBUG(dbgs() << " failed to decompose condition\n"); +/* TO_UPSTREAM(BoundsSafety) ON*/ + if (isBoundsSafetyAnnotated(CheckInst)) { + annotateMissedReplaceConditions(CheckInst, + cast(CheckInst)->isSigned(), + MaxAnalysisRecursionDepth - 1); + } +/* TO_UPSTREAM(BoundsSafety) OFF*/ + return std::nullopt; + } } auto &CSToUse = Info.getCS(R.IsSigned); @@ -1554,11 +2037,16 @@ void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B, // If the constraint has a pre-condition, skip the constraint if it does not // hold. SmallVector NewVariables; - auto R = getConstraint(Pred, A, B, NewVariables); - + auto R = getConstraint(Pred, A, B, NewVariables, true); // TODO: Support non-equality for facts as well. - if (!R.isValid(*this) || R.isNe()) + if (R.isNe()) return; + if (!R.isValid(*this)) { + NewVariables.clear(); + R = getConstraint(Pred, A, B, NewVariables, false); + if (!R.isValid(*this)) + return; + } LLVM_DEBUG(dbgs() << "Adding '"; dumpUnpackedICmp(dbgs(), Pred, A, B); dbgs() << "'\n"); @@ -1648,7 +2136,7 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info, SmallVectorImpl &ToRemove) { auto DoesConditionHold = [](CmpInst::Predicate Pred, Value *A, Value *B, ConstraintInfo &Info) { - auto R = Info.getConstraintForSolving(Pred, A, B); + auto R = Info.getConstraintForSolving(Pred, A, B, true); if (R.size() < 2 || !R.isValid(Info)) return false; @@ -1676,11 +2164,14 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, OptimizationRemarkEmitter &ORE) { bool Changed = false; DT.updateDFSNumbers(); + + const DataLayout &DL = F.getDataLayout(); + State S(DT, LI, SE, DL, F.getContext()); + S.collectPHIInductionVars(F); SmallVector FunctionArgs; for (Value &Arg : F.args()) FunctionArgs.push_back(&Arg); - ConstraintInfo Info(F.getDataLayout(), FunctionArgs); - State S(DT, LI, SE); + ConstraintInfo Info(S, FunctionArgs); std::unique_ptr ReproducerModule( DumpReproducers ? new Module(F.getName(), F.getContext()) : nullptr); @@ -1894,4 +2385,3 @@ PreservedAnalyses ConstraintEliminationPass::run(Function &F, PA.preserveSet(); return PA; } - diff --git a/llvm/lib/Transforms/Scalar/LoopTrapAnalysis.cpp b/llvm/lib/Transforms/Scalar/LoopTrapAnalysis.cpp new file mode 100644 index 0000000000000..334652549c7ab --- /dev/null +++ b/llvm/lib/Transforms/Scalar/LoopTrapAnalysis.cpp @@ -0,0 +1,174 @@ +//===-- LoopTrapAnalysis.cpp - Loop Trap Count pass -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/LoopTrapAnalysis.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Remarks/BoundsSafetyOptRemarks.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace llvm::ore; +#define DEBUG_TYPE "loop-trap-analysis" +#define REMARK_PASS DEBUG_TYPE + +enum class CheckLoopHoistType { MAYBE_CAN_HOIST, CANNOT_HOIST, SKIP }; +static cl::opt NewTrapSemantics( + "use-new-trap-semantics", cl::init(false), + cl::desc("Assume that traps are using the new trap semantics " + "logic.")); +static cl::opt BoundsSafetyTrapsOnly( + "use-bounds-safety-traps-only", cl::init(false), + cl::desc( + "We only check for -fbounds-safety traps if the flag is false we can check " + "for any hoistable traps.")); + +/// Check for an unreachable instruction that has an edge to any of \p L basic +/// blocks. if `--use-bounds-safety-traps-only` is used make sure that the trap and +/// branch instructions have -fbounds-safety annotation. +static bool hasUnreachableInst(Loop *L) { + SmallVector LoopExitBlocks; + L->getExitBlocks(LoopExitBlocks); + for (auto *BB : LoopExitBlocks) { + auto *I = BB->getTerminator(); + // check for trap instructions. If `BoundsSafetyTrapsOnly` is false then we + // ignore if the trap has a -fbounds-safety annotation. + if (!isa(I)) + continue; + if (BoundsSafetyTrapsOnly && !isBoundsSafetyAnnotated(I)) + continue; + if (any_of(predecessors(BB), [L](BasicBlock *PredB) { + auto *TerminatorInst = PredB->getTerminator(); + return L->contains(PredB) && + (isa(TerminatorInst) || + isa(TerminatorInst)) && + (!BoundsSafetyTrapsOnly || isBoundsSafetyAnnotated(TerminatorInst)); + })) + return true; + } + return false; +} + +static std::string getSideEffectReasons(const Instruction &I) { + std::string Buf; + raw_string_ostream OS(Buf); + if (isa(I) && I.mayReadOrWriteMemory()) + OS << "Instruction might have a volatile memory access"; + else { + if (I.mayWriteToMemory()) + OS << "Instruction may write to memory\n"; + if (I.mayThrow()) + OS << "Instruction may throw an exception\n"; + if (!I.willReturn()) + OS << "Instruction may not return\n"; + } + return Buf; +} + +/// Check if \p L can be hoisted or not and emit a detailed remark about why +/// it can't be hoisted. +static CheckLoopHoistType processLoops(Loop *L, ScalarEvolution &SE, + OptimizationRemarkEmitter &ORE) { + CheckLoopHoistType HoistType; + bool SymbolicMaxBackEdgeComputable = + !isa(SE.getSymbolicMaxBackedgeTakenCount(L)); + bool HasSideEffects = false; + if (!hasUnreachableInst(L)) + return CheckLoopHoistType::SKIP; + + SmallVector InstructionsWithSideEffects; + SmallVector SideEffectReasons; + for (auto *BB : L->blocks()) { + if (any_of(*BB, [&InstructionsWithSideEffects, + &SideEffectReasons](const Instruction &I) { + bool InstHasSideEffects = false; + // If a call instruction reads or writes to memory we don't know if + // the access is non-volatile so we asssume that the call instruction + // has side effects. + if (isa(I)) + InstHasSideEffects = + I.mayHaveSideEffects() || I.mayReadFromMemory(); + else if (NewTrapSemantics) + InstHasSideEffects = !I.willReturn() || I.mayThrow(); + else + InstHasSideEffects = I.mayHaveSideEffects(); + if (InstHasSideEffects) { + std::string Buf; + raw_string_ostream OS(Buf); + I.print(OS); + InstructionsWithSideEffects.push_back(Buf); + SideEffectReasons.push_back(getSideEffectReasons(I)); + } + return InstHasSideEffects; + })) { + + HasSideEffects = true; + break; + } + } + + HoistType = !HasSideEffects && SymbolicMaxBackEdgeComputable + ? CheckLoopHoistType::MAYBE_CAN_HOIST + : CheckLoopHoistType::CANNOT_HOIST; + // Emit a remark for the loop + auto ORA = OptimizationRemarkAnalysis(REMARK_PASS, "LoopTrap", + &L->getHeader()->front()); + ORA << "Loop: " << L->getName() << " "; + if (HoistType == CheckLoopHoistType::CANNOT_HOIST) { + ORA << "cannot be hoisted: \n"; + if (HasSideEffects) { + ORA << "\nThe following instructions have side effects:\n"; + for (unsigned Idx = 0; Idx < InstructionsWithSideEffects.size(); Idx++) { + ORA << "\t" << InstructionsWithSideEffects[Idx] << "\n"; + ORA << "Reason:\n"; + ORA << SideEffectReasons[Idx]; + } + } + if (!SymbolicMaxBackEdgeComputable) + ORA << "Backedge is not computable.\n"; + } else + ORA << "can be hoisted\n"; + ORE.emit(ORA); + return HoistType; +} + +/// Collect info for hoistable loop checks for \p F and report remarks for +/// individual loops and report a summary for hoistable checks for the function. +static void emitRemarks(Function &F, LoopInfo &LI, + OptimizationRemarkEmitter &ORE, ScalarEvolution &SE) { + unsigned TotalCanHoistLoops = 0; + unsigned TotalUnHoistableLoops = 0; + for (auto *L : LI.getLoopsInPreorder()) { + CheckLoopHoistType Type = processLoops(L, SE, ORE); + if (Type == CheckLoopHoistType::MAYBE_CAN_HOIST) + TotalCanHoistLoops++; + else if (Type == CheckLoopHoistType::CANNOT_HOIST) + TotalUnHoistableLoops++; + } + + OptimizationRemarkAnalysis Rem(REMARK_PASS, "LoopTrapSummary", &F); + Rem << "Trap checks results:\n"; + Rem << "Total count of loops with traps " + << NV("TotalCount", TotalCanHoistLoops + TotalUnHoistableLoops) << "\n"; + Rem << "Loops that maybe can be hoisted: " + << NV("CountHoist", TotalCanHoistLoops) << "\n"; + Rem << "Loops that cannot be hoisted: " + << NV("CountCannotHoist", TotalUnHoistableLoops) << "\n"; + ORE.emit(Rem); +} + +PreservedAnalyses LoopTrapAnalysisPass::run(Function &F, + FunctionAnalysisManager &AM) { + auto &LI = AM.getResult(F); + auto &SE = AM.getResult(F); + auto &ORE = AM.getResult(F); + emitRemarks(F, LI, ORE, SE); + return PreservedAnalyses::all(); +} \ No newline at end of file diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp index cbc35b6dd4292..453dcc57e8335 100644 --- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp +++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp @@ -58,6 +58,7 @@ #include "llvm/Transforms/Utils/LoopPeel.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopUtils.h" +#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/SizeOpts.h" #include "llvm/Transforms/Utils/UnrollLoop.h" #include @@ -219,6 +220,7 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences( UP.UnrollAndJam = false; UP.UnrollAndJamInnerLoopThreshold = 60; UP.MaxIterationsCountToAnalyze = UnrollMaxIterationsCountToAnalyze; + UP.SCEVExpansionBudget = SCEVCheapExpansionBudget; // Override with any target specific settings TTI.getUnrollingPreferences(L, SE, UP, &ORE); @@ -1350,6 +1352,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE, ULO.Runtime = UP.Runtime; ULO.ForgetAllSCEV = ForgetAllSCEV; ULO.Heart = getLoopConvergenceHeart(L); + ULO.SCEVExpansionBudget = UP.SCEVExpansionBudget; LoopUnrollResult UnrollResult = UnrollLoop( L, ULO, LI, &SE, &DT, &AC, &TTI, &ORE, PreserveLCSSA, &RemainderLoop, AA); if (UnrollResult == LoopUnrollResult::Unmodified) diff --git a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp index 6a681fd933971..da7693e189ca3 100644 --- a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp +++ b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp @@ -97,19 +97,6 @@ static DISubprogram *getSubprogram(DIScope *Scope) { return cast(Scope)->getSubprogram(); } -/// Erase \p V from \p BB and move \II forward to avoid invalidating -/// iterators. -static void eraseFromParentAndMove(Value *V, BasicBlock::reverse_iterator &II, - BasicBlock &BB) { - auto *Inst = cast(V); - // Still used, don't erase. - if (!Inst->use_empty()) - return; - if (II != BB.rend() && Inst == &*II) - ++II; - Inst->eraseFromParent(); -} - /// Return true if V is a splat of a value (which is used when multiplying a /// matrix with a scalar). static bool isSplat(Value *V) { @@ -259,7 +246,7 @@ static bool isUniformShape(Value *V) { /// Return the ShapeInfo for the result of \p I, it it can be determined. static std::optional computeShapeInfoForInst(Instruction *I, - const ValueMap &ShapeMap) { + const DenseMap &ShapeMap) { Value *M; Value *N; Value *K; @@ -492,10 +479,16 @@ class LowerMatrixIntrinsics { /// the result value of the instruction, with the only exceptions being store /// instructions and the matrix_column_major_store intrinsics. For those, the /// shape information indicates that those instructions should be lowered - /// using shape information as well. A ValueMap is used so that when - /// sub-passes like optimizeTransposes performs RAUW the map stays - /// up-to-date. - ValueMap ShapeMap; + /// using shape information as well. Note that extra care is needed when + /// erasing or RAUW'ing a value that is present in ShapeMap. If the + /// replacement is also a matrix operation, use + /// updateShapeAndReplaceAllUsesWith to make sure the replacement is added to + /// ShapeMap. We don't use ValueMap, as there are also cases where we do not + /// want to add shape information for a replacement instruction. When directly + /// erasing a value with an entry in ShapeMap, use + /// eraseFromParentAndRemoveFromShapeMap to make sure ShapeMap is also updated + /// accordingly. + DenseMap ShapeMap; /// List of instructions to remove. While lowering, we are not replacing all /// users of a lowered instruction, if shape information is available and @@ -759,6 +752,30 @@ class LowerMatrixIntrinsics { return Operation(T0, Shape0.t(), T1, Shape1.t()); } + /// Erase \p Inst from both ShapeMap (if an entry exists) and erase \p Inst + /// itself. + void eraseFromParentAndRemoveFromShapeMap(Instruction *Inst) { + auto Iter = ShapeMap.find(Inst); + if (Iter != ShapeMap.end()) + ShapeMap.erase(Iter); + Inst->eraseFromParent(); + } + + /// Erase \p V from \p BB and move \II forward to avoid invalidating + /// iterators. + void eraseFromParentAndMove(Value *V, BasicBlock::reverse_iterator &II, + BasicBlock &BB) { + auto *Inst = cast(V); + // Still used, don't erase. + if (!Inst->use_empty()) + return; + if (II != BB.rend() && Inst == &*II) + ++II; + eraseFromParentAndRemoveFromShapeMap(Inst); + } + + /// Add a new entry to ShapeMap for \p New with \p Old's shape info, erase the + /// entry for \p Old and replace all uses of \p Old with \p New. void updateShapeAndReplaceAllUsesWith(Instruction &Old, Value *New) { // We need to remove Old from the ShapeMap otherwise RAUW will replace it // with New. We should only add New it it supportsShapeInfo so we insert @@ -872,13 +889,13 @@ class LowerMatrixIntrinsics { void liftTranspose(Instruction &I) { // Erase dead Instructions after lifting transposes from binops. - auto CleanupBinOp = [](Instruction &T, Value *A, Value *B) { + auto CleanupBinOp = [this](Instruction &T, Value *A, Value *B) { if (T.use_empty()) - T.eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(&T); if (A->use_empty()) - cast(A)->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(cast(A)); if (A != B && B->use_empty()) - cast(B)->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(cast(B)); }; Value *A, *B, *AT, *BT; @@ -908,8 +925,7 @@ class LowerMatrixIntrinsics { match(B, m_Intrinsic( m_Value(BT), m_ConstantInt(), m_ConstantInt()))) { IRBuilder<> Builder(&I); - auto *Add = cast(Builder.CreateFAdd(AT, BT, "mfadd")); - setShapeInfo(Add, {R, C}); + auto *Add = Builder.CreateFAdd(AT, BT, "mfadd"); MatrixBuilder MBuilder(Builder); Instruction *NewInst = MBuilder.CreateMatrixTranspose( Add, R->getZExtValue(), C->getZExtValue(), "mfadd_t"); @@ -918,9 +934,13 @@ class LowerMatrixIntrinsics { computeShapeInfoForInst(&I, ShapeMap) && "Shape of new instruction doesn't match original shape."); CleanupBinOp(I, A, B); - assert(computeShapeInfoForInst(Add, ShapeMap).value_or(ShapeMap[Add]) == - ShapeMap[Add] && - "Shape of updated addition doesn't match cached shape."); + if (auto *AddI = dyn_cast(Add)) { + setShapeInfo(AddI, {R, C}); + assert( + computeShapeInfoForInst(AddI, ShapeMap).value_or(ShapeMap[AddI]) == + ShapeMap[AddI] && + "Shape of updated addition doesn't match cached shape."); + } } } @@ -1014,7 +1034,8 @@ class LowerMatrixIntrinsics { // Third, try to fuse candidates. for (CallInst *CI : MaybeFusableInsts) - LowerMatrixMultiplyFused(CI, FusedInsts, LifetimeEnds); + if (!FusedInsts.contains(CI)) + LowerMatrixMultiplyFused(CI, FusedInsts, LifetimeEnds); Changed = !FusedInsts.empty(); @@ -1475,7 +1496,7 @@ class LowerMatrixIntrinsics { m_Value(Arg)))) { auto *NewLoad = Builder.CreateLoad(Op->getType(), Arg); Op->replaceAllUsesWith(NewLoad); - cast(Op)->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(cast(Op)); return; } else if (match(Op, m_Intrinsic( m_Value(Arg)))) { @@ -1844,15 +1865,15 @@ class LowerMatrixIntrinsics { // Mark eliminated instructions as fused and remove them. FusedInsts.insert(Store); FusedInsts.insert(MatMul); - Store->eraseFromParent(); - MatMul->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(Store); + eraseFromParentAndRemoveFromShapeMap(MatMul); if (LoadOp0->hasNUses(0)) { FusedInsts.insert(LoadOp0); - LoadOp0->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(LoadOp0); } if (LoadOp1 != LoadOp0 && LoadOp1->hasNUses(0)) { FusedInsts.insert(LoadOp1); - LoadOp1->eraseFromParent(); + eraseFromParentAndRemoveFromShapeMap(LoadOp1); } } diff --git a/llvm/lib/Transforms/Scalar/MergedLoadStoreMotion.cpp b/llvm/lib/Transforms/Scalar/MergedLoadStoreMotion.cpp index 299239fb70200..cc67a455672be 100644 --- a/llvm/lib/Transforms/Scalar/MergedLoadStoreMotion.cpp +++ b/llvm/lib/Transforms/Scalar/MergedLoadStoreMotion.cpp @@ -84,6 +84,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" using namespace llvm; @@ -255,7 +256,8 @@ void MergedLoadStoreMotion::sinkStoresAndGEPs(BasicBlock *BB, StoreInst *S0, BasicBlock::iterator InsertPt = BB->getFirstInsertionPt(); // Intersect optional metadata. S0->andIRFlags(S1); - S0->dropUnknownNonDebugMetadata(); + + combineMetadataForCSE(S0, S1, true); S0->applyMergedLocation(S0->getDebugLoc(), S1->getDebugLoc()); S0->mergeDIAssignID(S1); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index 0e95e3c3a9e41..8b106e1b1e957 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -3295,18 +3295,22 @@ void llvm::combineMetadata(Instruction *K, const Instruction *J, K->mergeDIAssignID(J); break; case LLVMContext::MD_tbaa: - K->setMetadata(Kind, MDNode::getMostGenericTBAA(JMD, KMD)); + if (DoesKMove) + K->setMetadata(Kind, MDNode::getMostGenericTBAA(JMD, KMD)); break; case LLVMContext::MD_alias_scope: - K->setMetadata(Kind, MDNode::getMostGenericAliasScope(JMD, KMD)); + if (DoesKMove) + K->setMetadata(Kind, MDNode::getMostGenericAliasScope(JMD, KMD)); break; case LLVMContext::MD_noalias: case LLVMContext::MD_mem_parallel_loop_access: - K->setMetadata(Kind, MDNode::intersect(JMD, KMD)); + if (DoesKMove) + K->setMetadata(Kind, MDNode::intersect(JMD, KMD)); break; case LLVMContext::MD_access_group: - K->setMetadata(LLVMContext::MD_access_group, - intersectAccessGroups(K, J)); + if (DoesKMove) + K->setMetadata(LLVMContext::MD_access_group, + intersectAccessGroups(K, J)); break; case LLVMContext::MD_range: if (DoesKMove || !K->hasMetadata(LLVMContext::MD_noundef)) diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp index a0406111ecbf3..4cbe0dc8999df 100644 --- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp @@ -63,6 +63,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopUtils.h" +#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/SimplifyIndVar.h" #include "llvm/Transforms/Utils/UnrollLoop.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -592,10 +593,10 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI, : isEpilogProfitable(L); if (ULO.Runtime && - !UnrollRuntimeLoopRemainder(L, ULO.Count, ULO.AllowExpensiveTripCount, - EpilogProfitability, ULO.UnrollRemainder, - ULO.ForgetAllSCEV, LI, SE, DT, AC, TTI, - PreserveLCSSA, RemainderLoop)) { + !UnrollRuntimeLoopRemainder( + L, ULO.Count, ULO.AllowExpensiveTripCount, EpilogProfitability, + ULO.UnrollRemainder, ULO.ForgetAllSCEV, LI, SE, DT, AC, TTI, + PreserveLCSSA, ULO.SCEVExpansionBudget, RemainderLoop)) { if (ULO.Force) ULO.Runtime = false; else { diff --git a/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp b/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp index c7b88d3c48a69..97790a1e365d6 100644 --- a/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp @@ -48,6 +48,7 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/LoopUtils.h" +#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/UnrollLoop.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include @@ -243,7 +244,8 @@ llvm::UnrollAndJamLoop(Loop *L, unsigned Count, unsigned TripCount, if (!UnrollRuntimeLoopRemainder(L, Count, /*AllowExpensiveTripCount*/ false, /*UseEpilogRemainder*/ true, UnrollRemainder, /*ForgetAllSCEV*/ false, - LI, SE, DT, AC, TTI, true, EpilogueLoop)) { + LI, SE, DT, AC, TTI, true, + SCEVCheapExpansionBudget, EpilogueLoop)) { LLVM_DEBUG(dbgs() << "Won't unroll-and-jam; remainder loop could not be " "generated when assuming runtime trip count\n"); return LoopUnrollResult::Unmodified; diff --git a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp index 56aa96e550d9c..de939ab9eeec6 100644 --- a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp @@ -583,7 +583,8 @@ bool llvm::UnrollRuntimeLoopRemainder( Loop *L, unsigned Count, bool AllowExpensiveTripCount, bool UseEpilogRemainder, bool UnrollRemainder, bool ForgetAllSCEV, LoopInfo *LI, ScalarEvolution *SE, DominatorTree *DT, AssumptionCache *AC, - const TargetTransformInfo *TTI, bool PreserveLCSSA, Loop **ResultLoop) { + const TargetTransformInfo *TTI, bool PreserveLCSSA, + unsigned SCEVExpansionBudget, Loop **ResultLoop) { LLVM_DEBUG(dbgs() << "Trying runtime unrolling on Loop: \n"); LLVM_DEBUG(L->dump()); LLVM_DEBUG(UseEpilogRemainder ? dbgs() << "Using epilog remainder.\n" @@ -673,8 +674,8 @@ bool llvm::UnrollRuntimeLoopRemainder( const DataLayout &DL = Header->getDataLayout(); SCEVExpander Expander(*SE, DL, "loop-unroll"); if (!AllowExpensiveTripCount && - Expander.isHighCostExpansion(TripCountSC, L, SCEVCheapExpansionBudget, - TTI, PreHeaderBR)) { + Expander.isHighCostExpansion(TripCountSC, L, SCEVExpansionBudget, TTI, + PreHeaderBR)) { LLVM_DEBUG(dbgs() << "High cost for expanding trip count scev!\n"); return false; } diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h index c63cf0c37f2f9..f8728ad50cf49 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h +++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h @@ -457,6 +457,12 @@ class LoopVectorizationPlanner { bool isMoreProfitable(const VectorizationFactor &A, const VectorizationFactor &B) const; + /// Returns true if the per-lane cost of VectorizationFactor A is lower than + /// that of B in the context of vectorizing a loop with known \p MaxTripCount. + bool isMoreProfitable(const VectorizationFactor &A, + const VectorizationFactor &B, + const unsigned MaxTripCount) const; + /// Determines if we have the infrastructure to vectorize the loop and its /// epilogue, assuming the main loop is vectorized by \p VF. bool isCandidateForEpilogueVectorization(const ElementCount VF) const; diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index 68363abdb817a..df1e7d0fc7b0f 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -1554,7 +1554,10 @@ class LoopVectorizationCostModel { /// Returns true if epilogue vectorization is considered profitable, and /// false otherwise. /// \p VF is the vectorization factor chosen for the original loop. - bool isEpilogueVectorizationProfitable(const ElementCount VF) const; + /// \p Multiplier is an aditional scaling factor applied to VF before + /// comparing to EpilogueVectorizationMinVF. + bool isEpilogueVectorizationProfitable(const ElementCount VF, + const unsigned Multiplier) const; /// Returns the execution time cost of an instruction for a given vector /// width. Vector width of one means scalar. @@ -4293,12 +4296,11 @@ getVScaleForTuning(const Loop *L, const TargetTransformInfo &TTI) { } bool LoopVectorizationPlanner::isMoreProfitable( - const VectorizationFactor &A, const VectorizationFactor &B) const { + const VectorizationFactor &A, const VectorizationFactor &B, + const unsigned MaxTripCount) const { InstructionCost CostA = A.Cost; InstructionCost CostB = B.Cost; - unsigned MaxTripCount = PSE.getSE()->getSmallConstantMaxTripCount(OrigLoop); - // Improve estimate for the vector width if it is scalable. unsigned EstimatedWidthA = A.Width.getKnownMinValue(); unsigned EstimatedWidthB = B.Width.getKnownMinValue(); @@ -4347,6 +4349,13 @@ bool LoopVectorizationPlanner::isMoreProfitable( return CmpFn(RTCostA, RTCostB); } +bool LoopVectorizationPlanner::isMoreProfitable( + const VectorizationFactor &A, const VectorizationFactor &B) const { + const unsigned MaxTripCount = + PSE.getSE()->getSmallConstantMaxTripCount(OrigLoop); + return LoopVectorizationPlanner::isMoreProfitable(A, B, MaxTripCount); +} + static void emitInvalidCostRemarks(SmallVector InvalidCosts, OptimizationRemarkEmitter *ORE, Loop *TheLoop) { @@ -4626,7 +4635,7 @@ bool LoopVectorizationPlanner::isCandidateForEpilogueVectorization( } bool LoopVectorizationCostModel::isEpilogueVectorizationProfitable( - const ElementCount VF) const { + const ElementCount VF, const unsigned Multiplier) const { // FIXME: We need a much better cost-model to take different parameters such // as register pressure, code size increase and cost of extra branches into // account. For now we apply a very crude heuristic and only consider loops @@ -4641,9 +4650,6 @@ bool LoopVectorizationCostModel::isEpilogueVectorizationProfitable( if (TTI.getMaxInterleaveFactor(VF) <= 1) return false; - unsigned Multiplier = 1; - if (VF.isScalable()) - Multiplier = getVScaleForTuning(TheLoop, TTI).value_or(1); if ((Multiplier * VF.getKnownMinValue()) >= EpilogueVectorizationMinVF) return true; return false; @@ -4690,7 +4696,11 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor( return Result; } - if (!CM.isEpilogueVectorizationProfitable(MainLoopVF)) { + unsigned Multiplier = IC; + if (MainLoopVF.isScalable()) + Multiplier = getVScaleForTuning(OrigLoop, TTI).value_or(1); + + if (!CM.isEpilogueVectorizationProfitable(MainLoopVF, Multiplier)) { LLVM_DEBUG(dbgs() << "LEV: Epilogue vectorization is not profitable for " "this loop\n"); return Result; @@ -4709,16 +4719,20 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor( ScalarEvolution &SE = *PSE.getSE(); Type *TCType = Legal->getWidestInductionType(); const SCEV *RemainingIterations = nullptr; + unsigned MaxTripCount = 0; for (auto &NextVF : ProfitableVFs) { // Skip candidate VFs without a corresponding VPlan. if (!hasPlanWithVF(NextVF.Width)) continue; - // Skip candidate VFs with widths >= the estimate runtime VF (scalable - // vectors) or the VF of the main loop (fixed vectors). + // Skip candidate VFs with widths >= the (estimated) runtime VF (scalable + // vectors) or > the VF of the main loop (fixed vectors). if ((!NextVF.Width.isScalable() && MainLoopVF.isScalable() && ElementCount::isKnownGE(NextVF.Width, EstimatedRuntimeVF)) || - ElementCount::isKnownGE(NextVF.Width, MainLoopVF)) + (NextVF.Width.isScalable() && + ElementCount::isKnownGE(NextVF.Width, MainLoopVF)) || + (!NextVF.Width.isScalable() && !MainLoopVF.isScalable() && + ElementCount::isKnownGT(NextVF.Width, MainLoopVF))) continue; // If NextVF is greater than the number of remaining iterations, the @@ -4729,6 +4743,14 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor( const SCEV *TC = createTripCountSCEV(TCType, PSE, OrigLoop); RemainingIterations = SE.getURemExpr( TC, SE.getConstant(TCType, MainLoopVF.getKnownMinValue() * IC)); + MaxTripCount = MainLoopVF.getKnownMinValue() * IC - 1; + if (SE.isKnownPredicate(CmpInst::ICMP_ULT, RemainingIterations, + SE.getConstant(TCType, MaxTripCount))) { + MaxTripCount = + SE.getUnsignedRangeMax(RemainingIterations).getZExtValue(); + } + LLVM_DEBUG(dbgs() << "LEV: Maximum Trip Count for Epilogue: " + << MaxTripCount << "\n"); } if (SE.isKnownPredicate( CmpInst::ICMP_UGT, @@ -4737,7 +4759,8 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor( continue; } - if (Result.Width.isScalar() || isMoreProfitable(NextVF, Result)) + if (Result.Width.isScalar() || + isMoreProfitable(NextVF, Result, MaxTripCount)) Result = NextVF; } diff --git a/llvm/test/Analysis/ScalarEvolution/backedge-taken-count-guard-info-with-multiple-predecessors.ll b/llvm/test/Analysis/ScalarEvolution/backedge-taken-count-guard-info-with-multiple-predecessors.ll new file mode 100644 index 0000000000000..46dccf454f21a --- /dev/null +++ b/llvm/test/Analysis/ScalarEvolution/backedge-taken-count-guard-info-with-multiple-predecessors.ll @@ -0,0 +1,338 @@ +; RUN: opt < %s -disable-output "-passes=print" -scalar-evolution-max-iterations=0 -scalar-evolution-classify-expressions=0 2>&1 | FileCheck %s + +define void @slt(i16 %a, i16 %b, i1 %c) { +; CHECK-LABEL: 'slt' +; CHECK-NEXT: Determining loop execution counts for: @slt +; CHECK-NEXT: Loop %loop: backedge-taken count is (19 + (-1 * %count)) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 18 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (19 + (-1 * %count)) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp slt i16 %a, 1 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp slt i16 %b, 4 + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp sle i16 %count, 19 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, 1 + %exitcond = icmp eq i16 %iv.next, 20 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @ult(i16 %a, i16 %b, i1 %c) { +; CHECK-LABEL: 'ult' +; CHECK-NEXT: Determining loop execution counts for: @ult +; CHECK-NEXT: Loop %loop: backedge-taken count is (21 + (-1 * %count)) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 19 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (21 + (-1 * %count)) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp ult i16 %a, 2 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp ult i16 %b, 5 + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp ule i16 %count, 20 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, 1 + %exitcond = icmp eq i16 %iv.next, 22 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @sgt(i16 %a, i16 %b, i1 %c) { +; CHECK-LABEL: 'sgt' +; CHECK-NEXT: Determining loop execution counts for: @sgt +; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 9 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp sgt i16 %a, 10 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp sgt i16 %b, 8 + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp sge i16 %count, 1 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, -1 + %exitcond = icmp eq i16 %iv.next, 0 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @ugt(i16 %a, i16 %b, i1 %c) { +; CHECK-LABEL: 'ugt' +; CHECK-NEXT: Determining loop execution counts for: @ugt +; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 10 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp ugt i16 %a, 11 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp ugt i16 %b, 7 + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp ne i16 %count, 0 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, -1 + %exitcond = icmp eq i16 %iv.next, 0 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @three_incoming(i16 %a, i16 %b, i1 %c, i1 %d) { +; CHECK-LABEL: 'three_incoming' +; CHECK-NEXT: Determining loop execution counts for: @three_incoming +; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 11 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %entry2 + +entry2: + br i1 %d, label %b2, label %b3 + +b1: + %cmp1 = icmp ugt i16 %a, 10 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp ugt i16 %b, 8 + br i1 %cmp2, label %exit, label %preheader + +b3: + %cmp3 = icmp ugt i16 %b, 12 + br i1 %cmp3, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ], [ %b, %b3 ] + %cmp4 = icmp ne i16 %count, 0 + br i1 %cmp4, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, -1 + %exitcond = icmp eq i16 %iv.next, 0 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @mixed(i16 %a, i16 %b, i1 %c) { +; CHECK-LABEL: 'mixed' +; CHECK-NEXT: Determining loop execution counts for: @mixed +; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 -2 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp ugt i16 %a, 10 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp sgt i16 %b, 8 + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp ne i16 %count, 0 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, -1 + %exitcond = icmp eq i16 %iv.next, 0 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @one_constant(i16 %a, i16 %b, i1 %c, i16 %d) { +; CHECK-LABEL: 'one_constant' +; CHECK-NEXT: Determining loop execution counts for: @one_constant +; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i16 -2 +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %count) +; CHECK-NEXT: Loop %loop: Trip multiple is 1 +entry: + br i1 %c, label %b1, label %b2 + +b1: + %cmp1 = icmp ugt i16 %a, 10 + br i1 %cmp1, label %exit, label %preheader + +b2: + %cmp2 = icmp ugt i16 %b, %d + br i1 %cmp2, label %exit, label %preheader + +preheader: + %count = phi i16 [ %a, %b1 ], [ %b, %b2 ] + %cmp3 = icmp ne i16 %count, 0 + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i16 [ %iv.next, %loop ], [ %count, %preheader ] + %iv.next = add i16 %iv, -1 + %exitcond = icmp eq i16 %iv.next, 0 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +define void @epilogue(i64 %count) { +; CHECK-LABEL: 'epilogue' +; CHECK-NEXT: Determining loop execution counts for: @epilogue +; CHECK-NEXT: Loop %epilogue: backedge-taken count is (-1 + %count.epilogue) +; CHECK-NEXT: Loop %epilogue: constant max backedge-taken count is i64 6 +; CHECK-NEXT: Loop %epilogue: symbolic max backedge-taken count is (-1 + %count.epilogue) +; CHECK-NEXT: Loop %epilogue: Trip multiple is 1 +; CHECK-NEXT: Loop %while.body: backedge-taken count is ((-8 + %count) /u 8) +; CHECK-NEXT: Loop %while.body: constant max backedge-taken count is i64 2305843009213693951 +; CHECK-NEXT: Loop %while.body: symbolic max backedge-taken count is ((-8 + %count) /u 8) +; CHECK-NEXT: Loop %while.body: Trip multiple is 1 +entry: + %cmp = icmp ugt i64 %count, 7 + br i1 %cmp, label %while.body, label %epilogue.preheader + +while.body: + %iv = phi i64 [ %sub, %while.body ], [ %count, %entry ] + %sub = add i64 %iv, -8 + %exitcond.not = icmp ugt i64 %sub, 7 + br i1 %exitcond.not, label %while.body, label %while.loopexit + +while.loopexit: + %sub.exit = phi i64 [ %sub, %while.body ] + br label %epilogue.preheader + +epilogue.preheader: + %count.epilogue = phi i64 [ %count, %entry ], [ %sub.exit, %while.loopexit ] + %epilogue.cmp = icmp eq i64 %count.epilogue, 0 + br i1 %epilogue.cmp, label %exit, label %epilogue + +epilogue: + %iv.epilogue = phi i64 [ %dec, %epilogue ], [ %count.epilogue, %epilogue.preheader ] + %dec = add i64 %iv.epilogue, -1 + %exitcond.epilogue = icmp eq i64 %dec, 0 + br i1 %exitcond.epilogue, label %exit, label %epilogue + +exit: + ret void +} + +declare void @llvm.assume(i1) + +; Checks that the presence of assumptions does not interfere with +; exiting loop guard collection via following loop predecessors. +define void @pr120442(i1 %c.1, i1 %c.2) { +; CHECK-LABEL: 'pr120442' +; CHECK-NEXT: Determining loop execution counts for: @pr120442 +; CHECK-NEXT: Loop %inner.header: backedge-taken count is i32 0 +; CHECK-NEXT: Loop %inner.header: constant max backedge-taken count is i32 0 +; CHECK-NEXT: Loop %inner.header: symbolic max backedge-taken count is i32 0 +; CHECK-NEXT: Loop %inner.header: Trip multiple is 1 +entry: + call void @llvm.assume(i1 %c.1) + call void @llvm.assume(i1 %c.2) + br label %outer.header + +outer.header: + %phi7 = phi i32 [ 0, %bb ], [ 0, %entry ] + br label %inner.header + +bb: + br i1 false, label %outer.header, label %bb + +inner.header: + %phi = phi i32 [ %add, %inner.header ], [ 0, %outer.header ] + %add = add i32 %phi, 1 + %icmp = icmp ugt i32 %add, 0 + br i1 %icmp, label %exit, label %inner.header + +exit: + ret void +} + +; Checks correct traversal for loops without a unique predecessor +; outside the loop. +define void @pr120615() { +; CHECK-LABEL: pr120615 +; CHECK-NEXT: Determining loop execution counts for: @pr120615 +; CHECK-NEXT: Loop %header: backedge-taken count is i32 0 +; CHECK-NEXT: Loop %header: constant max backedge-taken count is i32 0 +; CHECK-NEXT: Loop %header: symbolic max backedge-taken count is i32 0 +; CHECK-NEXT: Loop %header: Trip multiple is 1 +entry: + br label %header + +bb: + br label %header + +header: + %0 = phi i32 [ %1, %header ], [ 0, %bb ], [ 0, %entry ] + %1 = add i32 %0, 1 + %icmp = icmp slt i32 %0, 0 + br i1 %icmp, label %header, label %exit + +exit: + ret void + +} diff --git a/llvm/test/CodeGen/AArch64/wincfi-missing-seh-directives.ll b/llvm/test/CodeGen/AArch64/wincfi-missing-seh-directives.ll new file mode 100644 index 0000000000000..2002c37cb2528 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/wincfi-missing-seh-directives.ll @@ -0,0 +1,86 @@ +; RUN: llc -mtriple=aarch64-windows %s --filetype obj -o /dev/null +; RUN: llc -mtriple=aarch64-windows %s --filetype asm -o - | FileCheck %s + +; Check that it doesn't crash and that each instruction in the +; prologue has a corresponding seh directive. +; +; CHECK-NOT: error: Incorrect size for +; CHECK: foo: +; CHECK: .seh_proc foo +; CHECK: sub sp, sp, #288 +; CHECK: .seh_stackalloc 288 +; CHECK: str x19, [sp] // 8-byte Folded Spill +; CHECK: .seh_save_reg x19, 0 +; CHECK: str x21, [sp, #8] // 8-byte Folded Spill +; CHECK: .seh_save_reg x21, 8 +; CHECK: stp x23, x24, [sp, #16] // 16-byte Folded Spill +; CHECK: .seh_save_regp x23, 16 +; CHECK: stp x25, x26, [sp, #32] // 16-byte Folded Spill +; CHECK: .seh_save_regp x25, 32 +; CHECK: stp x27, x28, [sp, #48] // 16-byte Folded Spill +; CHECK: .seh_save_regp x27, 48 +; CHECK: stp x29, x30, [sp, #64] // 16-byte Folded Spill +; CHECK: .seh_save_fplr 64 +; CHECK: sub sp, sp, #224 +; CHECK: .seh_stackalloc 224 +; CHECK: .seh_endprologue + +target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32" +target triple = "aarch64-unknown-windows-msvc19.42.34436" + +%swift.refcounted = type { ptr, i64 } +%TScA_pSg = type <{ [16 x i8] }> +%T5repro4TestVSg = type <{ [32 x i8] }> +%T5repro4TestV = type <{ %TSS, %TSS }> +%TSS = type <{ %Ts11_StringGutsV }> +%Ts11_StringGutsV = type <{ %Ts13_StringObjectV }> +%Ts13_StringObjectV = type <{ %Ts6UInt64V, ptr }> +%Ts6UInt64V = type <{ i64 }> + +declare swiftcc ptr @swift_task_alloc() + +declare swifttailcc void @bar(ptr, ptr, i64, i64, i64, ptr, i64, i64, i64, i64, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr, i64, ptr) + +define swifttailcc void @foo(ptr %0, ptr swiftasync %1, ptr swiftself %2, ptr %3, ptr %._guts2._object._object, ptr %.rid4._guts._object._object, ptr %4, ptr %.idx8, ptr %.idx8._guts._object._object, ptr %5, ptr %.rid9._guts._object._object, ptr %6) { +entry: + %7 = load i64, ptr null, align 8 + %8 = load i64, ptr %3, align 8 + %9 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 2 + %10 = load i64, ptr %9, align 8 + %11 = load ptr, ptr %1, align 8 + %12 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 3 + %13 = load i64, ptr %.rid9._guts._object._object, align 8 + %14 = load i64, ptr %.idx8._guts._object._object, align 8 + %15 = load i64, ptr %5, align 8 + %16 = getelementptr { i64, i64, i64, i64 }, ptr %12, i32 0, i32 3 + %17 = load i64, ptr %16, align 8 + %18 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 4 + %19 = load i64, ptr %18, align 8 + %.rid._guts._object._object = getelementptr %Ts13_StringObjectV, ptr %18, i32 0, i32 1 + %20 = load ptr, ptr %.rid._guts._object._object, align 8 + %21 = load i64, ptr %.rid4._guts._object._object, align 8 + %22 = load i64, ptr %0, align 8 + %23 = load ptr, ptr %6, align 8 + %24 = load i64, ptr %2, align 8 + %25 = load ptr, ptr %._guts2._object._object, align 8 + %26 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 7 + %27 = load i64, ptr %26, align 8 + %._guts3._object._object = getelementptr %Ts13_StringObjectV, ptr %26, i32 0, i32 1 + %28 = load ptr, ptr %._guts3._object._object, align 8 + %29 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 8 + %30 = load i64, ptr %29, align 8 + %.idx5 = getelementptr %T5repro4TestV, ptr %29, i32 0, i32 1 + %31 = load i64, ptr %.idx5, align 8 + %.idx5._guts._object._object = getelementptr %Ts13_StringObjectV, ptr %.idx5, i32 0, i32 1 + %32 = load ptr, ptr %.idx5._guts._object._object, align 8 + %33 = getelementptr <{ %swift.refcounted, %TScA_pSg, %TSS, %T5repro4TestVSg, %T5repro4TestV, %TSS, %TSS, %TSS, %T5repro4TestV, %TSS, %T5repro4TestV, %T5repro4TestV, %TSS }>, ptr %2, i32 0, i32 9 + %34 = load i64, ptr %33, align 8 + %35 = load i64, ptr %4, align 8 + %36 = load i64, ptr %.idx8, align 8 + %37 = load i64, ptr %1, align 8 + %38 = call swiftcc ptr @swift_task_alloc() + store ptr null, ptr %3, align 8 + store ptr null, ptr %4, align 8 + musttail call swifttailcc void @bar(ptr null, ptr swiftasync %.rid4._guts._object._object, i64 %7, i64 %8, i64 %10, ptr %5, i64 %13, i64 %14, i64 %15, i64 %17, i64 %19, ptr %20, i64 %21, ptr %.idx8, i64 %22, ptr %23, i64 %24, ptr %25, i64 %27, ptr %28, i64 %30, ptr %.idx8._guts._object._object, i64 %31, ptr %32, i64 %34, ptr %._guts2._object._object, i64 %35, ptr %2, i64 %36, ptr %1, i64 %37, ptr %0, i64 0, ptr null, i64 0, ptr null) + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir index 0386410d1b612..c434e14b30d15 100644 --- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir +++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir @@ -39,15 +39,16 @@ body: | ; CHECK: RETHROW 1 EH_LABEL %0:i32 = CATCH &__cpp_exception, implicit-def dead $arguments - RETHROW 0, implicit-def dead $arguments + RETHROW %bb.1, implicit-def dead $arguments bb.2 (landing-pad): + successors: %bb.3 ; CHECK: bb.2 (landing-pad): ; CHECK: CATCH ; CHECK: RETHROW 0 EH_LABEL %1:i32 = CATCH &__cpp_exception, implicit-def dead $arguments - RETHROW 0, implicit-def dead $arguments + RETHROW %bb.2, implicit-def dead $arguments bb.3: ; CHECK: bb.3: @@ -104,12 +105,14 @@ body: | RETURN %0:i32, implicit-def dead $arguments bb.3 (landing-pad): + successors: EH_LABEL %0:i32 = CATCH &__cpp_exception, implicit-def dead $arguments - RETHROW 0, implicit-def dead $arguments + RETHROW %bb.3, implicit-def dead $arguments bb.4 (landing-pad): + successors: EH_LABEL %1:i32 = CATCH &__cpp_exception, implicit-def dead $arguments - RETHROW 0, implicit-def dead $arguments + RETHROW %bb.4, implicit-def dead $arguments ... diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll index 3537baa425164..a0429f40b2540 100644 --- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll +++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll @@ -400,6 +400,107 @@ unreachable: ; preds = %rethrow unreachable } +; The bitcode below is generated when the code below is compiled and +; Temp::~Temp() is inlined into inlined_cleanupret(): +; +; void inlined_cleanupret() { +; try { +; Temp t; +; throw 2; +; } catch (...) +; } +; +; Temp::~Temp() { +; try { +; throw 1; +; } catch (...) { +; } +; } +; +; ~Temp() generates cleanupret, which is lowered to a 'rethrow' later. That +; rethrow's immediate argument should correctly target the top-level cleanuppad +; (catch_all). This is a regression test for the bug where we did not compute +; rethrow's argument correctly. + +; CHECK-LABEL: inlined_cleanupret: +; CHECK: try +; CHECK: call __cxa_throw +; CHECK: catch_all +; CHECK: try +; CHECK: try +; CHECK: call __cxa_throw +; CHECK: catch +; CHECK: call __cxa_end_catch +; CHECK: try +; CHECK: try +; Note that this rethrow targets the top-level catch_all +; CHECK: rethrow 4 +; CHECK: catch +; CHECK: try +; CHECK: call __cxa_end_catch +; CHECK: delegate 5 +; CHECK: return +; CHECK: end_try +; CHECK: delegate 3 +; CHECK: end_try +; CHECK: catch_all +; CHECK: call _ZSt9terminatev +; CHECK: end_try +; CHECK: end_try +define void @inlined_cleanupret() personality ptr @__gxx_wasm_personality_v0 { +entry: + %exception = tail call ptr @__cxa_allocate_exception(i32 4) + store i32 2, ptr %exception, align 16 + invoke void @__cxa_throw(ptr nonnull %exception, ptr nonnull @_ZTIi, ptr null) + to label %unreachable unwind label %ehcleanup + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + %exception.i = call ptr @__cxa_allocate_exception(i32 4) [ "funclet"(token %0) ] + store i32 1, ptr %exception.i, align 16 + invoke void @__cxa_throw(ptr nonnull %exception.i, ptr nonnull @_ZTIi, ptr null) [ "funclet"(token %0) ] + to label %unreachable unwind label %catch.dispatch.i + +catch.dispatch.i: ; preds = %ehcleanup + %1 = catchswitch within %0 [label %catch.start.i] unwind label %terminate.i + +catch.start.i: ; preds = %catch.dispatch.i + %2 = catchpad within %1 [ptr null] + %3 = tail call ptr @llvm.wasm.get.exception(token %2) + %4 = tail call i32 @llvm.wasm.get.ehselector(token %2) + %5 = call ptr @__cxa_begin_catch(ptr %3) [ "funclet"(token %2) ] + invoke void @__cxa_end_catch() [ "funclet"(token %2) ] + to label %invoke.cont.i unwind label %terminate.i + +invoke.cont.i: ; preds = %catch.start.i + catchret from %2 to label %_ZN4TempD2Ev.exit + +terminate.i: ; preds = %catch.start.i, %catch.dispatch.i + %6 = cleanuppad within %0 [] + call void @_ZSt9terminatev() [ "funclet"(token %6) ] + unreachable + +_ZN4TempD2Ev.exit: ; preds = %invoke.cont.i + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %_ZN4TempD2Ev.exit + %7 = catchswitch within none [label %catch.start] unwind to caller + +catch.start: ; preds = %catch.dispatch + %8 = catchpad within %7 [ptr null] + %9 = tail call ptr @llvm.wasm.get.exception(token %8) + %10 = tail call i32 @llvm.wasm.get.ehselector(token %8) + %11 = call ptr @__cxa_begin_catch(ptr %9) #8 [ "funclet"(token %8) ] + call void @__cxa_end_catch() [ "funclet"(token %8) ] + catchret from %8 to label %try.cont + +try.cont: ; preds = %catch.start + ret void + +unreachable: ; preds = %entry + unreachable +} + declare void @foo() declare void @bar(ptr) @@ -415,8 +516,12 @@ declare i32 @llvm.wasm.get.ehselector(token) #0 declare void @llvm.wasm.rethrow() #1 ; Function Attrs: nounwind declare i32 @llvm.eh.typeid.for(ptr) #0 +; Function Attrs: nounwind +declare ptr @__cxa_allocate_exception(i32) #0 declare ptr @__cxa_begin_catch(ptr) declare void @__cxa_end_catch() +; Function Attrs: noreturn +declare void @__cxa_throw(ptr, ptr, ptr) #1 declare void @_ZSt9terminatev() declare ptr @_ZN4TempD2Ev(ptr returned) diff --git a/llvm/test/CodeGen/WebAssembly/exception.mir b/llvm/test/CodeGen/WebAssembly/exception.mir index 895e8d8864ea2..a5f78c18db16a 100644 --- a/llvm/test/CodeGen/WebAssembly/exception.mir +++ b/llvm/test/CodeGen/WebAssembly/exception.mir @@ -105,7 +105,7 @@ body: | bb.2: successors: %bb.3 - CLEANUPRET implicit-def dead $arguments + CLEANUPRET %bb.1, implicit-def dead $arguments bb.3: RETURN implicit-def dead $arguments diff --git a/llvm/test/MC/ELF/rtti-proxy-gotpcrel.ll b/llvm/test/MC/ELF/rtti-proxy-gotpcrel.ll index 9f47469ff5e7e..8131bbf8c48a6 100644 --- a/llvm/test/MC/ELF/rtti-proxy-gotpcrel.ll +++ b/llvm/test/MC/ELF/rtti-proxy-gotpcrel.ll @@ -3,6 +3,10 @@ ; RUN: llc %s -mtriple=aarch64 -o - | FileCheck %s ; RUN: llc %s -mtriple=riscv64 -o - | FileCheck %s +; XFAIL: * +; See swiftlang/llvm PR 9339 / rdar//135050296 +; AArch64 support was disabled because of linker issues in 0614ff994a. + @vtable = dso_local unnamed_addr constant i32 trunc (i64 sub (i64 ptrtoint (ptr @rtti.proxy to i64), i64 ptrtoint (ptr @vtable to i64)) to i32), align 4 @vtable_with_offset = dso_local unnamed_addr constant [2 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @rtti.proxy to i64), i64 ptrtoint (ptr @vtable_with_offset to i64)) to i32)], align 4 @vtable_with_negative_offset = dso_local unnamed_addr constant [2 x i32] [ diff --git a/llvm/test/Transforms/ConstraintElimination/add-nuw.ll b/llvm/test/Transforms/ConstraintElimination/add-nuw.ll index a8a474e6d4502..94867bbaa63d5 100644 --- a/llvm/test/Transforms/ConstraintElimination/add-nuw.ll +++ b/llvm/test/Transforms/ConstraintElimination/add-nuw.ll @@ -423,6 +423,38 @@ if.end: ; preds = %entry ret i1 %f } +define i1 @test_add_nuw_zext(i8 %N, i4 %idx) { +; CHECK-LABEL: @test_add_nuw_zext( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i4 [[IDX:%.*]] to i8 +; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[IDX_EXT]], 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[IDX]], 3 +; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[C_2:%.*]] = icmp ule i8 [[ADD]], 3 +; CHECK-NEXT: [[XOR_1:%.*]] = xor i1 true, [[C_2]] +; CHECK-NEXT: ret i1 [[XOR_1]] +; CHECK: else: +; CHECK-NEXT: [[C_3:%.*]] = icmp ule i8 [[IDX_EXT]], 4 +; CHECK-NEXT: ret i1 [[C_3]] +; +entry: + %idx.ext = zext i4 %idx to i8 + %add = add nuw i8 %idx.ext, 1 + %c.1 = icmp ule i4 %idx, 3 + br i1 %c.1, label %then, label %else + +then: + %t.1 = icmp ule i8 %add, 4 + %c.2 = icmp ule i8 %add, 3 + %xor.1 = xor i1 %t.1, %c.2 + ret i1 %xor.1 + +else: + %c.3 = icmp ule i8 %idx.ext, 4 + ret i1 %c.3 +} + declare void @use(i1) define i1 @add_nuw_neg_pr54224_i16(i16 %a) { diff --git a/llvm/test/Transforms/ConstraintElimination/add.ll b/llvm/test/Transforms/ConstraintElimination/add.ll index d2504be22f3fe..c9161da7eadcb 100644 --- a/llvm/test/Transforms/ConstraintElimination/add.ll +++ b/llvm/test/Transforms/ConstraintElimination/add.ll @@ -288,5 +288,37 @@ if.end: ; preds = %entry ret void } +define i1 @test_add_zext(i8 %N, i4 %idx) { +; CHECK-LABEL: @test_add_zext( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i4 [[IDX:%.*]] to i8 +; CHECK-NEXT: [[ADD:%.*]] = add i8 [[IDX_EXT]], 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[IDX]], 3 +; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[T_1:%.*]] = icmp ule i8 [[ADD]], 4 +; CHECK-NEXT: [[C_2:%.*]] = icmp ule i8 [[ADD]], 3 +; CHECK-NEXT: [[XOR_1:%.*]] = xor i1 [[T_1]], [[C_2]] +; CHECK-NEXT: ret i1 [[XOR_1]] +; CHECK: else: +; CHECK-NEXT: [[C_3:%.*]] = icmp ule i8 [[IDX_EXT]], 4 +; CHECK-NEXT: ret i1 [[C_3]] +; +entry: + %idx.ext = zext i4 %idx to i8 + %add = add i8 %idx.ext, 1 + %c.1 = icmp ule i4 %idx, 3 + br i1 %c.1, label %then, label %else + +then: + %t.1 = icmp ule i8 %add, 4 + %c.2 = icmp ule i8 %add, 3 + %xor.1 = xor i1 %t.1, %c.2 + ret i1 %xor.1 + +else: + %c.3 = icmp ule i8 %idx.ext, 4 + ret i1 %c.3 +} declare void @use(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-binop-overflow.ll b/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-binop-overflow.ll new file mode 100644 index 0000000000000..854a7d90d31b6 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-binop-overflow.ll @@ -0,0 +1,316 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --version 2 + +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; Changed check lines for annotation to ensure annotations are attatched to the correct instruction. + +declare void @use(i1) + +define void @shl(ptr %a, i32 %n) { +; CHECK-LABEL: define void @shl +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +; CHECK-NEXT: [[MUL:%.*]] = shl i64 [[IDX_EXT]], 1, !annotation !0 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[MUL]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I_0]] to i64 +; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_ZEXT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %mul = shl i64 %idx.ext , 1 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %mul + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i.zext = zext i32 %i.0 to i64 + %ptr.iv = getelementptr inbounds i32, ptr %a, i64 %i.zext + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @shl_nuw(ptr %a, i32 %n) { +; CHECK-LABEL: define void @shl_nuw +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +; CHECK-NEXT: [[MUL:%.*]] = shl nuw i64 [[IDX_EXT]], 1 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[MUL]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I_0]] to i64 +; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_ZEXT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %mul = shl nuw i64 %idx.ext , 1 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %mul + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i.zext = zext i32 %i.0 to i64 + %ptr.iv = getelementptr inbounds i32, ptr %a, i64 %i.zext + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @mul(ptr %a, i32 %n) { +; CHECK-LABEL: define void @mul +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[IDX_EXT]], 2, !annotation !0 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[MUL]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I_0]] to i64 +; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_ZEXT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %mul = mul i64 %idx.ext, 2 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %mul + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i.zext = zext i32 %i.0 to i64 + %ptr.iv = getelementptr inbounds i32, ptr %a, i64 %i.zext + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @mul_nuw(ptr %a, i32 %n) { +; CHECK-LABEL: define void @mul_nuw +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[IDX_EXT]], 2 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[MUL]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I_0]] to i64 +; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_ZEXT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %mul = mul nuw i64 %idx.ext , 2 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %mul + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i.zext = zext i32 %i.0 to i64 + %ptr.iv = getelementptr inbounds i32, ptr %a, i64 %i.zext + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @add_ub_unsigned(i32 %n) { +; CHECK-LABEL: define void @add_ub_unsigned +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[N]], 1, !annotation !0 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[N]], [[ADD]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[CMP]]) +; CHECK-NEXT: ret void +; + %add = add i32 %n, 1 + %cmp = icmp ult i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + ret void +} + +define void @add_ub_nuw_unsigned(i32 %n) { +; CHECK-LABEL: define void @add_ub_nuw_unsigned +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[N]], 1 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: ret void +; + %add = add nuw i32 %n, 1 + %cmp = icmp ult i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + ret void +} + +define void @add_ub_signed(i32 %n) { +; CHECK-LABEL: define void @add_ub_signed +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[N]], 1, !annotation !3 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[N]], [[ADD]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[CMP]]) +; CHECK-NEXT: ret void +; + %add = add i32 %n, 1 + %cmp = icmp slt i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + ret void +} + +define void @add_ub_nsw_signed(i32 %n) { +; CHECK-LABEL: define void @add_ub_nsw_signed +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[N]], 1 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: ret void +; + %add = add nsw i32 %n, 1 + %cmp = icmp slt i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + ret void +} + +define void @mul_ub_signed(i32 %n) { +; CHECK-LABEL: define void @mul_ub_signed +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[POS:%.*]] = icmp sgt i32 [[N]], 0 +; CHECK-NEXT: br i1 [[POS]], label [[MAIN:%.*]], label [[END:%.*]] +; CHECK: main: +; CHECK-NEXT: [[ADD:%.*]] = mul i32 [[N]], 2, !annotation !3 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[N]], [[ADD]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[CMP]]) +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: ret void +; +entry: + %pos = icmp sgt i32 %n, 0 + br i1 %pos, label %main, label %end + +main: ; preds = %entry + %add = mul i32 %n, 2 + %cmp = icmp slt i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + br label %end + +end: ; preds = %main, %entry + ret void +} + +define void @mul_ub_nsw_signed(i32 %n) { +; CHECK-LABEL: define void @mul_ub_nsw_signed +; CHECK-SAME: (i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[POS:%.*]] = icmp sgt i32 [[N]], 0 +; CHECK-NEXT: br i1 [[POS]], label [[MAIN:%.*]], label [[END:%.*]] +; CHECK: main: +; CHECK-NEXT: [[ADD:%.*]] = mul nsw i32 [[N]], 2 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: ret void +; +entry: + %pos = icmp sgt i32 %n, 0 + br i1 %pos, label %main, label %end + +main: ; preds = %entry + %add = mul nsw i32 %n, 2 + %cmp = icmp slt i32 %n, %add, !annotation !0 + call void @use(i1 %cmp) + br label %end + +end: ; preds = %main, %entry + ret void +} + +!0 = !{!"bounds-safety-check-ptr-lt-upper-bound"} +!1 = !{!"bounds-safety-check-ptr-ge-lower-bound"} +;. +; CHECK: [[META0:![0-9]+]] = !{!1} +; CHECK: [[META1:![0-9]+]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +; CHECK: [[META2:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +; CHECK: [[META3:![0-9]+]] = !{!4} +; CHECK: [[META4:![0-9]+]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +;. diff --git a/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-ptr-induction.ll b/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-ptr-induction.ll new file mode 100644 index 0000000000000..1cf89a8bbdd64 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/bounds-safety-missed-ptr-induction.ll @@ -0,0 +1,115 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --version 2 +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; Changed check lines for annotation to ensure annotations are attatched to the correct instruction. + +declare void @use(i1) +define void @loop_gep_phi(ptr %a, i32 %n) { +; CHECK-LABEL: define void @loop_gep_phi +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +; CHECK-NEXT: [[N_1:%.*]] = sub i32 [[N]], 1 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ [[N_1]], [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: br i1 true, label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]], !annotation !0 +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]], !annotation !2 +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = sub nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %n_1 = sub i32 %n , 1 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ %n_1, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp uge i32 %i.0, 0 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = sub nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_step_size(ptr %a, i32 %n) { +; CHECK-LABEL: define void @loop_gep_phi_step_size +; CHECK-SAME: (ptr [[A:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[MUL:%.*]] = shl nsw i32 [[N]], 1, !annotation !3 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[MUL]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]], !annotation !5 +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]], !annotation !7 +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 2 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %mul = shl nsw i32 %n, 1 + %idx.ext = zext i32 %mul to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp uge i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr, !annotation !0 + %c.2 = icmp uge ptr %ptr.iv, %a, !annotation !1 + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 2 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +!0 = !{!"bounds-safety-check-ptr-lt-upper-bound"} +!1 = !{!"bounds-safety-check-ptr-ge-lower-bound"} +;. +; CHECK: [[META0:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !1} +; CHECK: [[META1:![0-9]+]] = !{!"bounds-safety-missed-optimization-phi-direction", !"Cannot remove bounds checks because the pointer induction variable and loop counter are not stepping in the same direction. Consider rewriting the loop counter to step in the same direction as the pointer induction variable to help the optimizer remove the access bound checks."} +; CHECK: [[META2:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound", !1} +; CHECK: [[META3:![0-9]+]] = !{!4} +; CHECK: [[META4:![0-9]+]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +; CHECK: [[META5:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !6} +; CHECK: [[META6:![0-9]+]] = !{!"bounds-safety-missed-optimization-phi-step-size", !"Cannot remove bound checks because the pointer induction variable and loop counter don't have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks"} +; CHECK: [[META7:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound", !6} +;. diff --git a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic.ll b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic.ll index a4d825b327969..6a8f53e3a3c87 100644 --- a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic.ll +++ b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic.ll @@ -630,3 +630,150 @@ ptr.check: exit: ret i4 3 } + +define i1 @test_gep_add_nuw_zext_1_const_cmp(i8* %ptr, i8 %idx) { +; CHECK-LABEL: @test_gep_add_nuw_zext_1_const_cmp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_10:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 10 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[ADD_1:%.*]] = add nuw nsw i64 [[IDX_EXT]], 1 +; CHECK-NEXT: [[PTR_IDX_1:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_1]] +; CHECK-NEXT: [[ADD_2:%.*]] = add nuw nsw i64 [[IDX_EXT]], 2 +; CHECK-NEXT: [[PTR_IDX_2:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_2]] +; CHECK-NEXT: [[C_0:%.*]] = icmp ule i8 [[IDX]], 9 +; CHECK-NEXT: br i1 [[C_0]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[PTR_IDX_2]], [[PTR_10]] +; CHECK-NEXT: [[XOR_1:%.*]] = xor i1 true, [[C_1]] +; CHECK-NEXT: ret i1 [[XOR_1]] +; CHECK: else: +; CHECK-NEXT: [[PTR_IDX_0:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[C_2:%.*]] = icmp ule ptr [[PTR_IDX_0]], [[PTR_10]] +; CHECK-NEXT: [[XOR_2:%.*]] = xor i1 false, [[C_2]] +; CHECK-NEXT: ret i1 [[XOR_2]] +; +entry: + %ptr.10 = getelementptr inbounds i8, i8* %ptr, i64 10 + %idx.ext = zext i8 %idx to i64 + %add.1 = add nuw nsw i64 %idx.ext, 1 + %ptr.idx.1 = getelementptr inbounds i8, i8* %ptr, i64 %add.1 + %add.2 = add nuw nsw i64 %idx.ext, 2 + %ptr.idx.2 = getelementptr inbounds i8, i8* %ptr, i64 %add.2 + %c.0 = icmp ule i8 %idx, 9 + br i1 %c.0, label %then, label %else + +then: + %t.1 = icmp ule i8* %ptr.idx.1, %ptr.10 + %c.1 = icmp ule i8* %ptr.idx.2, %ptr.10 + %xor.1 = xor i1 %t.1, %c.1 + ret i1 %xor.1 + +else: + %ptr.idx.0 = getelementptr inbounds i8, i8* %ptr, i64 %idx.ext + %f.1 = icmp ule i8* %ptr.idx.1, %ptr.10 + %c.2 = icmp ule i8* %ptr.idx.0, %ptr.10 + %xor.2 = xor i1 %f.1, %c.2 + ret i1 %xor.2 +} + +define i1 @test_gep_add_nuw_zext_2_const_cmp(i8* %ptr, i8 %idx) { +; CHECK-LABEL: @test_gep_add_nuw_zext_2_const_cmp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_10:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 10 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[ADD_1:%.*]] = add nuw nsw i64 [[IDX_EXT]], 1 +; CHECK-NEXT: [[PTR_IDX_1:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_1]] +; CHECK-NEXT: [[ADD_2:%.*]] = add nuw nsw i64 [[IDX_EXT]], 2 +; CHECK-NEXT: [[PTR_IDX_2:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_2]] +; CHECK-NEXT: [[ADD_3:%.*]] = add nuw nsw i64 [[IDX_EXT]], 3 +; CHECK-NEXT: [[PTR_IDX_3:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_3]] +; CHECK-NEXT: [[C_0:%.*]] = icmp ule i8 [[IDX]], 8 +; CHECK-NEXT: br i1 [[C_0]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[PTR_IDX_3]], [[PTR_10]] +; CHECK-NEXT: [[XOR_1:%.*]] = xor i1 true, true +; CHECK-NEXT: [[XOR_2:%.*]] = xor i1 [[XOR_1]], [[C_1]] +; CHECK-NEXT: ret i1 [[XOR_1]] +; CHECK: else: +; CHECK-NEXT: [[C_2:%.*]] = icmp ule ptr [[PTR_IDX_1]], [[PTR_10]] +; CHECK-NEXT: [[XOR_3:%.*]] = xor i1 false, false +; CHECK-NEXT: [[XOR_4:%.*]] = xor i1 [[XOR_3]], [[C_2]] +; CHECK-NEXT: ret i1 [[XOR_4]] +; +entry: + %ptr.10 = getelementptr inbounds i8, i8* %ptr, i64 10 + %idx.ext = zext i8 %idx to i64 + %add.1 = add nuw nsw i64 %idx.ext, 1 + %ptr.idx.1 = getelementptr inbounds i8, i8* %ptr, i64 %add.1 + %add.2 = add nuw nsw i64 %idx.ext, 2 + %ptr.idx.2 = getelementptr inbounds i8, i8* %ptr, i64 %add.2 + %add.3 = add nuw nsw i64 %idx.ext, 3 + %ptr.idx.3 = getelementptr inbounds i8, i8* %ptr, i64 %add.3 + %c.0 = icmp ule i8 %idx, 8 + br i1 %c.0, label %then, label %else + +then: + %t.1 = icmp ule i8* %ptr.idx.1, %ptr.10 + %t.2 = icmp ule i8* %ptr.idx.2, %ptr.10 + %c.1 = icmp ule i8* %ptr.idx.3, %ptr.10 + %xor.1 = xor i1 %t.1, %t.2 + %xor.2 = xor i1 %xor.1, %c.1 + ret i1 %xor.1 + +else: + %f.1 = icmp ule i8* %ptr.idx.2, %ptr.10 + %f.2 = icmp ule i8* %ptr.idx.3, %ptr.10 + %c.2 = icmp ule i8* %ptr.idx.1, %ptr.10 + %xor.3 = xor i1 %f.1, %f.2 + %xor.4 = xor i1 %xor.3, %c.2 + ret i1 %xor.4 +} + +define i1 @test_gep_add_nuw_zext_1_i8_upper(i8* %ptr, i8 %idx, i8 %N) { +; CHECK-LABEL: @test_gep_add_nuw_zext_1_i8_upper( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_POS:%.*]] = icmp sge i8 [[N:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[N_POS]]) +; CHECK-NEXT: [[PTR_10:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i8 [[N]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[ADD_1:%.*]] = add nuw nsw i64 [[IDX_EXT]], 1 +; CHECK-NEXT: [[PTR_IDX_1:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_1]] +; CHECK-NEXT: [[ADD_2:%.*]] = add nuw nsw i64 [[IDX_EXT]], 2 +; CHECK-NEXT: [[PTR_IDX_2:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[ADD_2]] +; CHECK-NEXT: [[C_0:%.*]] = icmp ult i8 [[IDX]], [[N]] +; CHECK-NEXT: br i1 [[C_0]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[PTR_IDX_2]], [[PTR_10]] +; CHECK-NEXT: [[XOR_1:%.*]] = xor i1 true, [[C_1]] +; CHECK-NEXT: ret i1 [[XOR_1]] +; CHECK: else: +; CHECK-NEXT: [[PTR_IDX_0:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[C_2:%.*]] = icmp ule ptr [[PTR_IDX_0]], [[PTR_10]] +; CHECK-NEXT: [[XOR_2:%.*]] = xor i1 false, [[C_2]] +; CHECK-NEXT: ret i1 [[XOR_2]] +; +entry: + %N.pos = icmp sge i8 %N, 0 + call void @llvm.assume(i1 %N.pos) + %ptr.10 = getelementptr inbounds i8, i8* %ptr, i8 %N + %idx.ext = zext i8 %idx to i64 + %add.1 = add nuw nsw i64 %idx.ext, 1 + %ptr.idx.1 = getelementptr inbounds i8, i8* %ptr, i64 %add.1 + %add.2 = add nuw nsw i64 %idx.ext, 2 + %ptr.idx.2 = getelementptr inbounds i8, i8* %ptr, i64 %add.2 + %c.0 = icmp ult i8 %idx, %N + br i1 %c.0, label %then, label %else + +then: + %t.1 = icmp ule i8* %ptr.idx.1, %ptr.10 + %c.1 = icmp ule i8* %ptr.idx.2, %ptr.10 + %xor.1 = xor i1 %t.1, %c.1 + ret i1 %xor.1 + +else: + %ptr.idx.0 = getelementptr inbounds i8, i8* %ptr, i64 %idx.ext + %f.1 = icmp ule i8* %ptr.idx.1, %ptr.10 + %c.2 = icmp ule i8* %ptr.idx.0, %ptr.10 + %xor.2 = xor i1 %f.1, %c.2 + ret i1 %xor.2 +} diff --git a/llvm/test/Transforms/ConstraintElimination/gep-chains.ll b/llvm/test/Transforms/ConstraintElimination/gep-chains.ll index 5e2416462bddc..680de01b3f95a 100644 --- a/llvm/test/Transforms/ConstraintElimination/gep-chains.ll +++ b/llvm/test/Transforms/ConstraintElimination/gep-chains.ll @@ -285,10 +285,8 @@ define i1 @gep_add_1_ult_var_idx_only_inner_inbounds(ptr %dst, ptr %upper, i8 %l ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX]] to i16 ; CHECK-NEXT: [[DST_ADD_IDX:%.*]] = getelementptr inbounds i8, ptr [[DST]], i16 [[IDX_EXT]] ; CHECK-NEXT: [[DST_ADD_IDX_1:%.*]] = getelementptr i8, ptr [[DST_ADD_IDX]], i64 1 -; CHECK-NEXT: [[CMP_IDX_1:%.*]] = icmp ult ptr [[DST_ADD_IDX_1]], [[UPPER]] ; CHECK-NEXT: [[DST_ADD_IDX_2:%.*]] = getelementptr i8, ptr [[DST_ADD_IDX]], i64 2 -; CHECK-NEXT: [[CMP_IDX_2:%.*]] = icmp ult ptr [[DST_ADD_IDX_2]], [[UPPER]] -; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[CMP_IDX_1]], [[CMP_IDX_2]] +; CHECK-NEXT: [[RES_1:%.*]] = xor i1 true, true ; CHECK-NEXT: [[DST_ADD_IDX_3:%.*]] = getelementptr i8, ptr [[DST_ADD_IDX]], i64 3 ; CHECK-NEXT: [[CMP_IDX_3:%.*]] = icmp ult ptr [[DST_ADD_IDX_3]], [[UPPER]] ; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[CMP_IDX_3]] diff --git a/llvm/test/Transforms/ConstraintElimination/gep-constexpr.ll b/llvm/test/Transforms/ConstraintElimination/gep-constexpr.ll index cd57e0713417b..95813d30b4d64 100644 --- a/llvm/test/Transforms/ConstraintElimination/gep-constexpr.ll +++ b/llvm/test/Transforms/ConstraintElimination/gep-constexpr.ll @@ -41,3 +41,123 @@ entry: %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 0, i64 2) ret i1 %c.1 } + +define noundef i1 @gep_constexpr_index_lt_upper_no_inbounds(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_lt_upper_no_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr i16, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: ret i1 true +; +entry: + %cmp = icmp ult i32 %i, 3 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr i16, ptr @arr1, i64 %idxprom + %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) + ret i1 %c.1 +} + +define i1 @gep_constexpr_index_lt_upper_no_inbounds_not_noundef(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_lt_upper_no_inbounds_not_noundef( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr i16, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[UPPER]], getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) +; CHECK-NEXT: ret i1 [[C_1]] +; +entry: + %cmp = icmp ult i32 %i, 3 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr i16, ptr @arr1, i64 %idxprom + %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) + ret i1 %c.1 +} + +declare void @use(i1) + +define noundef i1 @gep_constexpr_index_lt_upper_no_inbounds_(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_lt_upper_no_inbounds_( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i16, ptr @arr1, i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr @arr1, [[GEP_1]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr i16, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: [[C_2:%.*]] = icmp ult ptr [[UPPER]], getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) +; CHECK-NEXT: ret i1 [[C_2]] +; +entry: + %gep.1 = getelementptr i16, ptr @arr1, i64 1 + %c.1 = icmp ule ptr @arr1, %gep.1 + call void @use(i1 %c.1) + %cmp = icmp ult i32 %i, 3 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr i16, ptr @arr1, i64 %idxprom + %c.2 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) + ret i1 %c.2 +} + +define noundef i1 @gep_constexpr_index_may_be_gt_upper_no_inbounds(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_may_be_gt_upper_no_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i16, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[UPPER]], getelementptr inbounds ([3 x i16], ptr @arr1, i64 0, i64 2) +; CHECK-NEXT: ret i1 [[C_1]] +; +entry: + %cmp = icmp ult i32 %i, 3 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr inbounds i16, ptr @arr1, i64 %idxprom + %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 0, i64 2) + ret i1 %c.1 +} + +define noundef i1 @gep_constexpr_index_lt_upper_no_inbounds_i8_gep(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_lt_upper_no_inbounds_i8_gep( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 6 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr i8, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: ret i1 true +; +entry: + %cmp = icmp ult i32 %i, 6 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr i8, ptr @arr1, i64 %idxprom + %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) + ret i1 %c.1 +} + +define noundef i1 @gep_constexpr_index_may_be_gt_upper_no_inbounds_i8_gep(i32 noundef %i) { +; CHECK-LABEL: @gep_constexpr_index_may_be_gt_upper_no_inbounds_i8_gep( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 7 +; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[I]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr i8, ptr @arr1, i64 [[IDXPROM]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[UPPER]], getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) +; CHECK-NEXT: ret i1 [[C_1]] +; +entry: + %cmp = icmp ult i32 %i, 7 + call void @llvm.assume(i1 %cmp) + %idxprom = zext i32 %i to i64 + %upper = getelementptr i8, ptr @arr1, i64 %idxprom + %c.1 = icmp ult ptr %upper, getelementptr inbounds ([3 x i16], ptr @arr1, i64 1, i64 0) + ret i1 %c.1 +} diff --git a/llvm/test/Transforms/ConstraintElimination/gep-gep-plus-one.ll b/llvm/test/Transforms/ConstraintElimination/gep-gep-plus-one.ll new file mode 100644 index 0000000000000..ba8bfca397850 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/gep-gep-plus-one.ll @@ -0,0 +1,141 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +%struct.value_entry_s = type { ptr, ptr } + +define noundef i1 @test_const_offset_gep_gep_plus_1_known_1(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_gep_plus_1_known_1( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 1 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: ret i1 true +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 1 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + +define noundef i1 @test_const_offset_gep_gep_plus_1_known_2(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_gep_plus_1_known_2( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: call void @is_noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 0 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: ret i1 false +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + call void @is_noundef(ptr %upper) + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 0 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %src + ret i1 %c.1 +} + +define noundef i1 @test_const_offset_gep_gep_plus_1_known_3(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_gep_plus_1_known_3( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 9 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: ret i1 true +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 9 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + +define noundef i1 @test_const_offset_gep_field_0_gep_plus_1_known(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_field_0_gep_plus_1_known( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 9, i32 0 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: ret i1 true +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 9, i32 0 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + + +define noundef i1 @test_const_offset_gep_field_1_gep_plus_1_not_known(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_field_1_gep_plus_1_not_known( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 9, i32 1 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_1]], [[UPPER]] +; CHECK-NEXT: ret i1 [[C_1]] +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 9, i32 1 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + +declare void @is_noundef(ptr noundef) + +define noundef i1 @test_const_offset_gep_plus_1_not_known_inbounds_1(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_plus_1_not_known_inbounds_1( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 10, i32 1 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_1]], [[UPPER]] +; CHECK-NEXT: ret i1 [[C_1]] +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 10, i32 1 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + +define noundef i1 @test_const_offset_gep_plus_1_not_known_inbounds_2(ptr %src) { +; CHECK-LABEL: @test_const_offset_gep_plus_1_not_known_inbounds_2( +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 10 +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 11 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_1]], [[UPPER]] +; CHECK-NEXT: ret i1 [[C_1]] +; + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 10 + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 11 + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 +} + +define noundef i1 @_value_entry_table_get11(i64 %idx, i64 %N, ptr %src) { +; CHECK-LABEL: @_value_entry_table_get11( +; CHECK-NEXT: [[IDX_ULT_N:%.*]] = icmp ult i64 [[IDX:%.*]], [[N:%.*]] +; CHECK-NEXT: br i1 [[IDX_ULT_N]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_VALUE_ENTRY_S:%.*]], ptr [[SRC:%.*]], i64 [[N]] +; CHECK-NEXT: call void @is_noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[SRC_PLUS_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC]], i64 [[IDX]] +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT_VALUE_ENTRY_S]], ptr [[SRC_PLUS_1]], i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_1]], [[UPPER]] +; CHECK-NEXT: ret i1 [[C_1]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; + %idx.ult.N = icmp ult i64 %idx, %N + br i1 %idx.ult.N, label %then, label %else + +then: + %upper = getelementptr inbounds %struct.value_entry_s, ptr %src, i64 %N + call void @is_noundef(ptr %upper) + %src.plus.1 = getelementptr %struct.value_entry_s, ptr %src, i64 %idx + %gep.1 = getelementptr %struct.value_entry_s, ptr %src.plus.1, i64 1 + %c.1 = icmp ule ptr %gep.1, %upper + ret i1 %c.1 + +else: + ret i1 false +} + +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-different-index-sizes.ll b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-different-index-sizes.ll new file mode 100644 index 0000000000000..3f8cb9de29368 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-different-index-sizes.ll @@ -0,0 +1,147 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; Tests for using inbounds information from GEPs with different index types. + +declare void @noundef(ptr noundef) +declare void @llvm.assume(i1) + +define i1 @inbounds_poison_is_ub_different_index_types_1(ptr %src, i16 %n, i16 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub_different_index_types_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_EXT:%.*]] = zext i16 [[N:%.*]] to i32 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i16 [[IDX:%.*]] to i32 +; CHECK-NEXT: [[N_EXT_64:%.*]] = zext i32 [[N_EXT]] to i64 +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX_EXT]], [[N_EXT]] +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i16 [[IDX]], 0 +; CHECK-NEXT: [[N_POS:%.*]] = icmp sge i16 [[N]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_POS]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[N_POS]]) +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT_64]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[SRC_IDX_1:%.*]] = getelementptr i32, ptr [[SRC]], i32 [[IDX_EXT]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: [[UPPER_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[N_EXT_64]] +; CHECK-NEXT: [[SRC_IDX_2:%.*]] = getelementptr i32, ptr [[SRC]], i32 [[IDX_EXT]] +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX_2]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; +entry: + %n.ext = zext i16 %n to i32 + %idx.ext = zext i16 %idx to i32 + %n.ext.64 = zext i32 %n.ext to i64 + %cmp.idx = icmp ult i32 %idx.ext, %n.ext + %idx.pos = icmp sge i16 %idx, 0 + %n.pos = icmp sge i16 %n, 0 + call void @llvm.assume(i1 %idx.pos) + call void @llvm.assume(i1 %n.pos) + %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext.64 + call void @noundef(ptr %upper) + br i1 %cmp.idx, label %then, label %else + +then: + %src.idx.1 = getelementptr i32, ptr %src, i32 %idx.ext + %cmp.upper.1 = icmp ule ptr %src.idx.1, %upper + ret i1 %cmp.upper.1 + +else: + %upper.2 = getelementptr inbounds i32, ptr %src, i64 %n.ext.64 + %src.idx.2 = getelementptr i32, ptr %src, i32 %idx.ext + %cmp.upper.2 = icmp ule ptr %src.idx.2, %upper + ret i1 %cmp.upper.2 +} + +define i1 @inbounds_poison_is_ub_different_index_types_2(ptr %src, i16 %n, i16 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub_different_index_types_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_EXT:%.*]] = zext i16 [[N:%.*]] to i32 +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i16 [[IDX:%.*]], [[N]] +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i16 [[IDX]], 0 +; CHECK-NEXT: [[N_POS:%.*]] = icmp sge i16 [[N]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_POS]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[N_POS]]) +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i32 [[N_EXT]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[UPPER_1:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i32 1 +; CHECK-NEXT: call void @noundef(ptr [[UPPER_1]]) +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[SRC_IDX_1:%.*]] = getelementptr i32, ptr [[SRC]], i16 [[IDX]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: [[SRC_IDX_2:%.*]] = getelementptr i32, ptr [[SRC]], i16 [[IDX]] +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX_2]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; +entry: + %n.ext = zext i16 %n to i32 + %cmp.idx = icmp ult i16 %idx, %n + %idx.pos = icmp sge i16 %idx, 0 + %n.pos = icmp sge i16 %n, 0 + call void @llvm.assume(i1 %idx.pos) + call void @llvm.assume(i1 %n.pos) + %upper = getelementptr inbounds i32, ptr %src, i32 %n.ext + call void @noundef(ptr %upper) + %upper.1 = getelementptr inbounds i32, ptr %src, i32 1 + call void @noundef(ptr %upper.1) + br i1 %cmp.idx, label %then, label %else + +then: + %src.idx.1 = getelementptr i32, ptr %src, i16 %idx + %cmp.upper.1 = icmp ule ptr %src.idx.1, %upper + ret i1 %cmp.upper.1 + +else: + %src.idx.2 = getelementptr i32, ptr %src, i16 %idx + %cmp.upper.2 = icmp ule ptr %src.idx.2, %upper + ret i1 %cmp.upper.2 +} + +define i1 @inbounds_poison_is_ub_different_index_types_3(ptr %src, i16 %n, i16 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub_different_index_types_3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i16 [[IDX:%.*]] to i32 +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i16 [[IDX]], [[N:%.*]] +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i16 [[IDX]], 0 +; CHECK-NEXT: [[N_POS:%.*]] = icmp sge i16 [[N]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_POS]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[N_POS]]) +; CHECK-NEXT: [[UPPER_1:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i32 1 +; CHECK-NEXT: call void @noundef(ptr [[UPPER_1]]) +; CHECK-NEXT: [[UPPER_N:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i16 [[N]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER_N]]) +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[SRC_IDX_1:%.*]] = getelementptr i32, ptr [[SRC]], i32 [[IDX_EXT]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: [[SRC_IDX_2:%.*]] = getelementptr i32, ptr [[SRC]], i32 [[IDX_EXT]] +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX_2]], [[UPPER_N]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; +entry: + %idx.ext = zext i16 %idx to i32 + %cmp.idx = icmp ult i16 %idx, %n + %idx.pos = icmp sge i16 %idx, 0 + %n.pos = icmp sge i16 %n, 0 + call void @llvm.assume(i1 %idx.pos) + call void @llvm.assume(i1 %n.pos) + %upper.1 = getelementptr inbounds i32, ptr %src, i32 1 + call void @noundef(ptr %upper.1) + %upper.n = getelementptr inbounds i32, ptr %src, i16 %n + call void @noundef(ptr %upper.n) + br i1 %cmp.idx, label %then, label %else + +then: + %src.idx.1 = getelementptr i32, ptr %src, i32 %idx.ext + %cmp.upper.1 = icmp ule ptr %src.idx.1, %upper.n + ret i1 %cmp.upper.1 + +else: + %src.idx.2 = getelementptr i32, ptr %src, i32 %idx.ext + %cmp.upper.2 = icmp ule ptr %src.idx.2, %upper.n + ret i1 %cmp.upper.2 +} diff --git a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-signed-preconds.ll b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-signed-preconds.ll new file mode 100644 index 0000000000000..8a1eb4dbf6bb1 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-signed-preconds.ll @@ -0,0 +1,342 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +declare void @use(i1 noundef) + +define void @test_idx_known_positive(ptr noundef %buf, i32 noundef %len, i32 %idx) { +; CHECK-LABEL: @test_idx_known_positive( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_SLT_LEN:%.*]] = icmp slt i32 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i32 [[IDX]], 0 +; CHECK-NEXT: [[PRECONDS:%.*]] = and i1 [[IDX_SLT_LEN]], [[IDX_POS]] +; CHECK-NEXT: br i1 [[PRECONDS]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[AND:%.*]] = and i1 true, true +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.slt.len = icmp slt i32 %idx, %len + %idx.pos = icmp sge i32 %idx, 0 + %preconds = and i1 %idx.slt.len, %idx.pos + br i1 %preconds, label %then , label %else + +then: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.ext = zext i32 %idx to i64 + %gep.idx = getelementptr i32, ptr %buf, i64 %idx.ext + %t.1 = icmp ult ptr %gep.idx, %add.ptr + %t.2 = icmp uge ptr %gep.idx, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + +define void @test_idx_not_known_positive(ptr noundef %buf, i32 noundef %len, i32 %idx) { +; CHECK-LABEL: @test_idx_not_known_positive( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_SLT_LEN:%.*]] = icmp slt i32 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[IDX_SLT_LEN]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[T_1:%.*]] = icmp ult ptr [[GEP_IDX]], [[ADD_PTR]] +; CHECK-NEXT: [[T_2:%.*]] = icmp uge ptr [[GEP_IDX]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[T_1]], [[T_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.slt.len = icmp slt i32 %idx, %len + br i1 %idx.slt.len, label %then , label %else + +then: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.ext = zext i32 %idx to i64 + %gep.idx = getelementptr i32, ptr %buf, i64 %idx.ext + %t.1 = icmp ult ptr %gep.idx, %add.ptr + %t.2 = icmp uge ptr %gep.idx, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + + + +define void @test_iv_known_positive(ptr noundef %buf, i32 noundef %len) { +; CHECK-LABEL: @test_iv_known_positive( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] +; CHECK: loop.header: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[LOOP_LATCH]], label [[EXIT:%.*]] +; CHECK: loop.latch: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IV_EXT:%.*]] = zext i32 [[IV]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IV_EXT]] +; CHECK-NEXT: [[AND:%.*]] = and i1 true, true +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; CHECK-NEXT: br label [[LOOP_HEADER]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ] + %cmp = icmp slt i32 %iv, %len + br i1 %cmp, label %loop.latch, label %exit + +loop.latch: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %iv.ext = zext i32 %iv to i64 + %gep.idx = getelementptr i32, ptr %buf, i64 %iv.ext + %t.1 = icmp ult ptr %gep.idx, %add.ptr + %t.2 = icmp uge ptr %gep.idx, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + %iv.next = add nuw nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @test_iv_not_known_positive_1(ptr noundef %buf, i32 noundef %len) { +; CHECK-LABEL: @test_iv_not_known_positive_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] +; CHECK: loop.header: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ -3, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[LOOP_LATCH]], label [[EXIT:%.*]] +; CHECK: loop.latch: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IV_EXT:%.*]] = zext i32 [[IV]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IV_EXT]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_IDX]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[GEP_IDX]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_1]], [[C_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; CHECK-NEXT: br label [[LOOP_HEADER]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ -3, %entry ], [ %iv.next, %loop.latch ] + %cmp = icmp slt i32 %iv, %len + br i1 %cmp, label %loop.latch, label %exit + +loop.latch: ; preds = %loop.header + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %iv.ext = zext i32 %iv to i64 + %gep.idx = getelementptr i32, ptr %buf, i64 %iv.ext + %c.1 = icmp ult ptr %gep.idx, %add.ptr + %c.2 = icmp uge ptr %gep.idx, %buf + %and = and i1 %c.1, %c.2 + call void @use(i1 %and) + %iv.next = add nuw nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @test_iv_not_known_positive_2(ptr noundef %buf, i32 noundef %len, i32 %start) { +; CHECK-LABEL: @test_iv_not_known_positive_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] +; CHECK: loop.header: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[LOOP_LATCH]], label [[EXIT:%.*]] +; CHECK: loop.latch: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IV_EXT:%.*]] = zext i32 [[IV]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IV_EXT]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_IDX]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[GEP_IDX]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_1]], [[C_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; CHECK-NEXT: br label [[LOOP_HEADER]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop.latch ] + %cmp = icmp slt i32 %iv, %len + br i1 %cmp, label %loop.latch, label %exit + +loop.latch: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %iv.ext = zext i32 %iv to i64 + %gep.idx = getelementptr i32, ptr %buf, i64 %iv.ext + %c.1 = icmp ult ptr %gep.idx, %add.ptr + %c.2 = icmp uge ptr %gep.idx, %buf + %and = and i1 %c.1, %c.2 + call void @use(i1 %and) + %iv.next = add nuw nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @test_multiple_variable_idx_known_positive_1(ptr noundef %buf, i32 noundef %len, i32 %idx.1, i32 %idx.2) { +; CHECK-LABEL: @test_multiple_variable_idx_known_positive_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_SLT_4:%.*]] = icmp slt i32 [[IDX_1:%.*]], 4 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_SLT_4]]) +; CHECK-NEXT: [[IDX_1_POS:%.*]] = icmp sge i32 [[IDX_1]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_POS]]) +; CHECK-NEXT: [[IDX_2_SLT_3:%.*]] = icmp slt i32 [[IDX_2:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_SLT_3]]) +; CHECK-NEXT: [[IDX_2_POS:%.*]] = icmp sge i32 [[IDX_2]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_POS]]) +; CHECK-NEXT: [[LEN_POS:%.*]] = icmp sge i32 [[LEN:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[LEN_POS]]) +; CHECK-NEXT: [[C_SLT_LEN:%.*]] = icmp slt i32 8, [[LEN]] +; CHECK-NEXT: br i1 [[C_SLT_LEN]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = sext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_1_EXT]] +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = sext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP_IDX_2:%.*]] = getelementptr i32, ptr [[GEP_IDX_1]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T_1:%.*]] = icmp ult ptr [[GEP_IDX_2]], [[ADD_PTR]] +; CHECK-NEXT: [[T_2:%.*]] = icmp uge ptr [[GEP_IDX_2]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[T_1]], [[T_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.1.slt.4 = icmp slt i32 %idx.1, 4 + call void @llvm.assume(i1 %idx.1.slt.4) + %idx.1.pos = icmp sge i32 %idx.1, 0 + call void @llvm.assume(i1 %idx.1.pos) + %idx.2.slt.3 = icmp slt i32 %idx.2, 3 + call void @llvm.assume(i1 %idx.2.slt.3) + %idx.2.pos = icmp sge i32 %idx.2, 0 + call void @llvm.assume(i1 %idx.2.pos) + %len.pos = icmp sge i32 %len, 0 + call void @llvm.assume(i1 %len.pos) + %c.slt.len = icmp slt i32 8, %len + br i1 %c.slt.len, label %then , label %else + +then: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.1.ext = sext i32 %idx.1 to i64 + %gep.idx.1 = getelementptr i32, ptr %buf, i64 %idx.1.ext + %idx.2.ext = sext i32 %idx.2 to i64 + %gep.idx.2 = getelementptr i32, ptr %gep.idx.1, i64 %idx.2.ext + %t.1 = icmp ult ptr %gep.idx.2, %add.ptr + %t.2 = icmp uge ptr %gep.idx.2, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + +define void @test_multiple_variable_idx_known_positive_2(ptr noundef %buf, i32 noundef %len, i32 %idx.1, i32 %idx.2) { +; CHECK-LABEL: @test_multiple_variable_idx_known_positive_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_SLT_4:%.*]] = icmp slt i32 [[IDX_1:%.*]], 4 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_SLT_4]]) +; CHECK-NEXT: [[IDX_1_POS:%.*]] = icmp sge i32 [[IDX_1]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_POS]]) +; CHECK-NEXT: [[IDX_2_SLT_3:%.*]] = icmp slt i32 [[IDX_2:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_SLT_3]]) +; CHECK-NEXT: [[IDX_2_POS:%.*]] = icmp sge i32 [[IDX_2]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_POS]]) +; CHECK-NEXT: [[LEN_POS:%.*]] = icmp sge i32 [[LEN:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[LEN_POS]]) +; CHECK-NEXT: [[C_SLT_LEN:%.*]] = icmp slt i32 4, [[LEN]] +; CHECK-NEXT: br i1 [[C_SLT_LEN]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = sext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_1_EXT]] +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = sext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP_IDX_2:%.*]] = getelementptr i32, ptr [[GEP_IDX_1]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T_1:%.*]] = icmp ult ptr [[GEP_IDX_2]], [[ADD_PTR]] +; CHECK-NEXT: [[T_2:%.*]] = icmp uge ptr [[GEP_IDX_2]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[T_1]], [[T_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.1.slt.4 = icmp slt i32 %idx.1, 4 + call void @llvm.assume(i1 %idx.1.slt.4) + %idx.1.pos = icmp sge i32 %idx.1, 0 + call void @llvm.assume(i1 %idx.1.pos) + %idx.2.slt.3 = icmp slt i32 %idx.2, 3 + call void @llvm.assume(i1 %idx.2.slt.3) + %idx.2.pos = icmp sge i32 %idx.2, 0 + call void @llvm.assume(i1 %idx.2.pos) + %len.pos = icmp sge i32 %len, 0 + call void @llvm.assume(i1 %len.pos) + %c.slt.len = icmp slt i32 4, %len + br i1 %c.slt.len, label %then , label %else + +then: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.1.ext = sext i32 %idx.1 to i64 + %gep.idx.1 = getelementptr i32, ptr %buf, i64 %idx.1.ext + %idx.2.ext = sext i32 %idx.2 to i64 + %gep.idx.2 = getelementptr i32, ptr %gep.idx.1, i64 %idx.2.ext + %t.1 = icmp ult ptr %gep.idx.2, %add.ptr + %t.2 = icmp uge ptr %gep.idx.2, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-structs.ll b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-structs.ll new file mode 100644 index 0000000000000..704a5651ce6bc --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-structs.ll @@ -0,0 +1,771 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; Tests for using inbounds information from GEPs. + +declare void @noundef(ptr noundef) + +%struct.1 = type { i32, i64, i8 } + +define i1 @struct_gep_dimensions_known_inbounds(ptr %start, ptr %high, i32 %idx) { +; CHECK-LABEL: @struct_gep_dimensions_known_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_1:%.*]], ptr [[START:%.*]], i64 6, i32 0 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[ADD_PTR]], [[HIGH:%.*]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_1]], ptr [[START]], i64 5, i32 0 +; CHECK-NEXT: ret i1 true +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr inbounds %struct.1, ptr %start, i64 6, i32 0 + %c.1 = icmp ule ptr %add.ptr, %high + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %start.0 = getelementptr %struct.1, ptr %start, i64 5, i32 0 + %c.0 = icmp ult ptr %start.0, %high + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +%struct.array = type { [4 x i8] } + +define i8 @struct_gep_dimensions_known_inbounds2(ptr %a) { +; CHECK-LABEL: @struct_gep_dimensions_known_inbounds2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [[STRUCT_ARRAY:%.*]], ptr [[A:%.*]], i64 0, i32 0, i64 0 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_ARRAY]], ptr [[A]], i64 0, i32 0, i64 4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_ARRAY]], ptr [[A]], i64 0, i32 0, i64 1 +; CHECK-NEXT: [[OR_COND35:%.*]] = and i1 true, true +; CHECK-NEXT: br i1 [[OR_COND35]], label [[EXIT:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i8 0 +; CHECK: exit: +; CHECK-NEXT: [[L:%.*]] = load i8, ptr [[ARRAYDECAY]], align 1 +; CHECK-NEXT: ret i8 [[L]] +; +entry: + %arraydecay = getelementptr inbounds %struct.array, ptr %a, i64 0, i32 0, i64 0 + %upper = getelementptr inbounds %struct.array, ptr %a, i64 0, i32 0, i64 4 + %0 = getelementptr %struct.array, ptr %a, i64 0, i32 0, i64 1 + %1 = icmp ult ptr %0, %upper + %2 = icmp uge ptr %0, %arraydecay + %or.cond35 = and i1 %1, %2 + br i1 %or.cond35, label %exit, label %trap + +trap: + ret i8 0 + +exit: + %l = load i8, ptr %arraydecay, align 1 + ret i8 %l +} +%struct.array2 = type { [4 x i8], [4 x i8]} +define i8 @struct_gep_dimensions_known_inbounds3(ptr %a) { +; CHECK-LABEL: @struct_gep_dimensions_known_inbounds3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2:%.*]], ptr [[A:%.*]], i64 0, i32 0, i64 0 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2]], ptr [[A]], i64 0, i32 1, i64 4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_ARRAY2]], ptr [[A]], i64 0, i32 1, i64 3 +; CHECK-NEXT: [[OR_COND35:%.*]] = and i1 true, true +; CHECK-NEXT: br i1 [[OR_COND35]], label [[EXIT:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i8 0 +; CHECK: exit: +; CHECK-NEXT: [[L:%.*]] = load i8, ptr [[ARRAYDECAY]], align 1 +; CHECK-NEXT: ret i8 [[L]] +; +entry: + %arraydecay = getelementptr inbounds %struct.array2, ptr %a, i64 0, i32 0, i64 0 + %upper = getelementptr inbounds %struct.array2, ptr %a, i64 0, i32 1, i64 4 + %0 = getelementptr %struct.array2, ptr %a, i64 0, i32 1, i64 3 + %1 = icmp ult ptr %0, %upper + %2 = icmp uge ptr %0, %arraydecay + %or.cond35 = and i1 %1, %2 + br i1 %or.cond35, label %exit, label %trap + +trap: + ret i8 0 + +exit: + %l = load i8, ptr %arraydecay, align 1 + ret i8 %l +} + +; this should trap given that the struct index for %0 > %upper +define i8 @struct_gep_dimensions_known_inbounds4(ptr %a) { +; CHECK-LABEL: @struct_gep_dimensions_known_inbounds4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2:%.*]], ptr [[A:%.*]], i64 0, i32 0, i64 0 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2]], ptr [[A]], i64 0, i32 0, i64 4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_ARRAY2]], ptr [[A]], i64 0, i32 1, i64 3 +; CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]] +; CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARRAYDECAY]] +; CHECK-NEXT: [[OR_COND35:%.*]] = and i1 [[TMP1]], [[TMP2]] +; CHECK-NEXT: br i1 [[OR_COND35]], label [[EXIT:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i8 0 +; CHECK: exit: +; CHECK-NEXT: [[L:%.*]] = load i8, ptr [[ARRAYDECAY]], align 1 +; CHECK-NEXT: ret i8 [[L]] +; +entry: + %arraydecay = getelementptr inbounds %struct.array2, ptr %a, i64 0, i32 0, i64 0 + %upper = getelementptr inbounds %struct.array2, ptr %a, i64 0, i32 0, i64 4 + %0 = getelementptr %struct.array2, ptr %a, i64 0, i32 1, i64 3 + %1 = icmp ult ptr %0, %upper + %2 = icmp uge ptr %0, %arraydecay + %or.cond35 = and i1 %1, %2 + br i1 %or.cond35, label %exit, label %trap + +trap: + ret i8 0 + +exit: + %l = load i8, ptr %arraydecay, align 1 + ret i8 %l +} + +define i8 @struct_gep_multi_dimensions_with_var_const_idx(ptr %a, i64 %idx) { +; CHECK-LABEL: @struct_gep_multi_dimensions_with_var_const_idx( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2:%.*]], ptr [[A:%.*]], i64 0, i32 0, i64 0 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_ARRAY2]], ptr [[A]], i64 [[IDX:%.*]], i32 0, i64 4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_ARRAY2]], ptr [[A]], i64 [[IDX]], i32 0, i64 3 +; CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]] +; CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARRAYDECAY]] +; CHECK-NEXT: [[OR_COND35:%.*]] = and i1 [[TMP1]], [[TMP2]] +; CHECK-NEXT: br i1 [[OR_COND35]], label [[EXIT:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i8 0 +; CHECK: exit: +; CHECK-NEXT: [[L:%.*]] = load i8, ptr [[ARRAYDECAY]], align 1 +; CHECK-NEXT: ret i8 [[L]] +; +entry: + %arraydecay = getelementptr inbounds %struct.array2, ptr %a, i64 0, i32 0, i64 0 + %upper = getelementptr inbounds %struct.array2, ptr %a, i64 %idx, i32 0, i64 4 + %0 = getelementptr %struct.array2, ptr %a, i64 %idx, i32 0, i64 3 + %1 = icmp ult ptr %0, %upper + %2 = icmp uge ptr %0, %arraydecay + %or.cond35 = and i1 %1, %2 + br i1 %or.cond35, label %exit, label %trap + +trap: + ret i8 0 + +exit: + %l = load i8, ptr %arraydecay, align 1 + ret i8 %l +} + +define i1 @struct_gep_first_dimension_may_not_be_inbounds(ptr %start, ptr %high, i32 %idx) { +; CHECK-LABEL: @struct_gep_first_dimension_may_not_be_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_1:%.*]], ptr [[START:%.*]], i64 6, i32 0 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[ADD_PTR]], [[HIGH:%.*]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_1]], ptr [[START]], i64 7, i32 0 +; CHECK-NEXT: [[C_0:%.*]] = icmp ult ptr [[START_0]], [[HIGH]] +; CHECK-NEXT: ret i1 [[C_0]] +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr inbounds %struct.1, ptr %start, i64 6, i32 0 + %c.1 = icmp ule ptr %add.ptr, %high + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %start.0 = getelementptr %struct.1, ptr %start, i64 7, i32 0 + %c.0 = icmp ult ptr%start.0, %high + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +%struct.2 = type { i32, [20 x i64], i8 } + +; TODO: Should keep and use multiple upper bounds. +define i1 @all_dimension_known_inbounds(ptr %start, ptr %high, i32 %idx) { +; CHECK-LABEL: @all_dimension_known_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT_PLUS_1:%.*]] = add nuw nsw i32 [[IDX:%.*]], 1 +; CHECK-NEXT: [[IDX_EXT_PLUS_1_EXT:%.*]] = zext i32 [[IDX_EXT_PLUS_1]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[ADD_PTR]], [[HIGH:%.*]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 5, i32 1, i64 5 +; CHECK-NEXT: ret i1 true +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %idx.ext.plus.1 = add nuw nsw i32 %idx, 1 + %idx.ext.plus.1.ext = zext i32 %idx.ext.plus.1 to i64 + %add.ptr = getelementptr inbounds %struct.2, ptr %start, i64 6, i32 1, i64 5 + %c.1 = icmp ule ptr %add.ptr, %high + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, ptr %start, i64 5, i32 1, i64 5 + %c.0 = icmp ult ptr %start.0, %high + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +define i1 @first_dimension_may_not_be_inbounds_2(ptr %start, ptr %high, i32 %idx) { +; CHECK-LABEL: @first_dimension_may_not_be_inbounds_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT_PLUS_1:%.*]] = add nuw nsw i32 [[IDX:%.*]], 1 +; CHECK-NEXT: [[IDX_EXT_PLUS_1_EXT:%.*]] = zext i32 [[IDX_EXT_PLUS_1]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[ADD_PTR]], [[HIGH:%.*]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 7, i32 1, i64 3 +; CHECK-NEXT: [[C_0:%.*]] = icmp ult ptr [[START_0]], [[HIGH]] +; CHECK-NEXT: ret i1 [[C_0]] +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %idx.ext.plus.1 = add nuw nsw i32 %idx, 1 + %idx.ext.plus.1.ext = zext i32 %idx.ext.plus.1 to i64 + %add.ptr = getelementptr inbounds %struct.2, ptr %start, i64 6, i32 1, i64 5 + %c.1 = icmp ule ptr %add.ptr, %high + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, ptr %start, i64 7, i32 1, i64 3 + %c.0 = icmp ult ptr %start.0, %high + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +define i1 @third_dimension_may_not_be_inbounds(ptr %start, ptr %high, i32 %idx) { +; CHECK-LABEL: @third_dimension_may_not_be_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT_PLUS_1:%.*]] = add nuw nsw i32 [[IDX:%.*]], 1 +; CHECK-NEXT: [[IDX_EXT_PLUS_1_EXT:%.*]] = zext i32 [[IDX_EXT_PLUS_1]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[ADD_PTR]], [[HIGH:%.*]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 5, i32 1, i64 6 +; CHECK-NEXT: ret i1 true +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %idx.ext.plus.1 = add nuw nsw i32 %idx, 1 + %idx.ext.plus.1.ext = zext i32 %idx.ext.plus.1 to i64 + %add.ptr = getelementptr inbounds %struct.2, ptr %start, i64 6, i32 1, i64 5 + %c.1 = icmp ule ptr %add.ptr, %high + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, ptr %start, i64 5, i32 1, i64 6 + %c.0 = icmp ult ptr %start.0, %high + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +%struct = type { i64, i64, i16 } + +; Test case where the inbounds GEP only indexes the first dimension. %gep uses +; the same first index, but accesses the third struct field. Computing %gep may +; overflow, due to adding the field offset and should not be de-composed. +define i1 @inbounds_first_struct_idx_compared_to_gep_with_const_second_idx(i8 %len, ptr %ptr) { +; CHECK-LABEL: @inbounds_first_struct_idx_compared_to_gep_with_const_second_idx( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i8 [[LEN:%.*]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT:%.*]], ptr [[PTR:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i64 [[LEN_EXT]], i32 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult ptr [[GEP]], [[UPPER]] +; CHECK-NEXT: br i1 [[CMP]], label [[EXIT_0:%.*]], label [[EXIT_1:%.*]] +; CHECK: exit.0: +; CHECK-NEXT: ret i1 true +; CHECK: exit.1: +; CHECK-NEXT: ret i1 false +; +entry: + %len.ext = zext i8 %len to i64 + %upper = getelementptr inbounds %struct, ptr %ptr, i64 %len.ext + %gep = getelementptr %struct, ptr %ptr, i64 %len.ext, i32 2 + %cmp = icmp ult ptr %gep, %upper + br i1 %cmp, label %exit.0, label %exit.1 + +exit.0: + ret i1 %cmp + +exit.1: + ret i1 0 +} + +; Similar to @inbounds_first_struct_idx_compared_to_gep_with_const_second_idx, +; but with a SGE check to ensure the upper bound offset is non-negative. +define i1 @inbounds_first_struct_idx_compared_to_gep_with_const_second_idx_2(i8 %len, ptr %ptr) { +; CHECK-LABEL: @inbounds_first_struct_idx_compared_to_gep_with_const_second_idx_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COUNT_POSITIVE_I:%.*]] = icmp sge i8 [[LEN:%.*]], 0 +; CHECK-NEXT: br i1 [[COUNT_POSITIVE_I]], label [[THEN:%.*]], label [[EXIT_1:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i8 [[LEN]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT:%.*]], ptr [[PTR:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i64 [[LEN_EXT]], i32 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult ptr [[GEP]], [[UPPER]] +; CHECK-NEXT: br i1 [[CMP]], label [[EXIT_0:%.*]], label [[EXIT_1]] +; CHECK: exit.0: +; CHECK-NEXT: ret i1 true +; CHECK: exit.1: +; CHECK-NEXT: ret i1 false +; +entry: + %count.positive.i = icmp sge i8 %len, 0 + br i1 %count.positive.i, label %then, label %exit.1 + +then: + %len.ext = zext i8 %len to i64 + %upper = getelementptr inbounds %struct, ptr %ptr, i64 %len.ext + %gep = getelementptr %struct, ptr %ptr, i64 %len.ext, i32 2 + %cmp = icmp ult ptr %gep, %upper + br i1 %cmp, label %exit.0, label %exit.1 + +exit.0: + ret i1 %cmp + +exit.1: + ret i1 0 +} + +; The inbounds GEP %upper accesses the same field as %gep, hence %gep can not overflow. +define i1 @inbounds_last_struct_field_compared_to_gep_with_const_second_idx(i8 %len, ptr %ptr) { +; CHECK-LABEL: @inbounds_last_struct_field_compared_to_gep_with_const_second_idx( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i8 [[LEN:%.*]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT:%.*]], ptr [[PTR:%.*]], i64 [[LEN_EXT]], i32 2 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i64 [[LEN_EXT]], i32 2 +; CHECK-NEXT: br i1 false, label [[EXIT_0:%.*]], label [[EXIT_1:%.*]] +; CHECK: exit.0: +; CHECK-NEXT: ret i1 false +; CHECK: exit.1: +; CHECK-NEXT: ret i1 false +; +entry: + %len.ext = zext i8 %len to i64 + %upper = getelementptr inbounds %struct, ptr %ptr, i64 %len.ext, i32 2 + %gep = getelementptr %struct, ptr %ptr, i64 %len.ext, i32 2 + %cmp = icmp ult i16* %gep, %upper + br i1 %cmp, label %exit.0, label %exit.1 + +exit.0: + ret i1 %cmp + +exit.1: + ret i1 0 +} + +; The inbounds GEP %upper accesses the field after %gep, hence %gep can not overflow. +define i1 @inbounds_last_struct_field_compared_to_gep_with_const_second_idx_2(i8 %len, ptr %ptr) { +; CHECK-LABEL: @inbounds_last_struct_field_compared_to_gep_with_const_second_idx_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i8 [[LEN:%.*]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT:%.*]], ptr [[PTR:%.*]], i64 [[LEN_EXT]], i32 2 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i64 [[LEN_EXT]], i32 1 +; CHECK-NEXT: br i1 true, label [[EXIT_0:%.*]], label [[EXIT_1:%.*]] +; CHECK: exit.0: +; CHECK-NEXT: ret i1 true +; CHECK: exit.1: +; CHECK-NEXT: ret i1 false +; +entry: + %len.ext = zext i8 %len to i64 + %upper = getelementptr inbounds %struct, ptr %ptr, i64 %len.ext, i32 2 + %gep = getelementptr %struct, ptr %ptr, i64 %len.ext, i32 1 + %cmp = icmp ult ptr %gep, %upper + br i1 %cmp, label %exit.0, label %exit.1 + +exit.0: + ret i1 %cmp + +exit.1: + ret i1 0 +} + +; %idx is not related to %len, so we cannot determine inbounds for %gep.1 or %gep.2. +; %cmp cannot be simplified. +define i1 @geps_struct_var_indices_not_related_to_upper_bound_not_guaranteed_inbounds(i8 %len, i8 %idx, ptr %ptr) { +; CHECK-LABEL: @geps_struct_var_indices_not_related_to_upper_bound_not_guaranteed_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i8 [[LEN:%.*]] to i16 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX:%.*]] to i16 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT:%.*]], ptr [[PTR:%.*]], i16 [[LEN_EXT]], i32 2 +; CHECK-NEXT: call void @noundef(ptr noundef [[UPPER]]) +; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i16 [[IDX_EXT]], 1 +; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i16 [[IDX_EXT]], i32 1 +; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr [[STRUCT]], ptr [[PTR]], i16 [[ADD]], i32 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult ptr [[GEP_1]], [[GEP_2]] +; CHECK-NEXT: br i1 [[CMP]], label [[EXIT_0:%.*]], label [[EXIT_1:%.*]] +; CHECK: exit.0: +; CHECK-NEXT: ret i1 true +; CHECK: exit.1: +; CHECK-NEXT: ret i1 false +; +entry: + %len.ext = zext i8 %len to i16 + %idx.ext = zext i8 %idx to i16 + %upper = getelementptr inbounds %struct, ptr %ptr, i16 %len.ext, i32 2 + call void @noundef(ptr noundef %upper) + %add = add nuw nsw i16 %idx.ext, 1 + %gep.1 = getelementptr %struct, ptr %ptr, i16 %idx.ext, i32 1 + %gep.2 = getelementptr %struct, ptr %ptr, i16 %add, i32 1 + %cmp = icmp ult ptr %gep.1, %gep.2 + br i1 %cmp, label %exit.0, label %exit.1 + +exit.0: + ret i1 %cmp + +exit.1: + ret i1 false +} + +%struct.buf_var_size = type { i32, [0 x i32] } + +define noundef i1 @test_var_size_buffer_gep_inbounds_1(ptr noundef %bp, i32 %idx, i32 %len) { +; CHECK-LABEL: @test_var_size_buffer_gep_inbounds_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds [[STRUCT_BUF_VAR_SIZE:%.*]], ptr [[BP:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[IDX_EXT]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %idx, %len + br i1 %cmp, label %then, label %else + +then: + %arr = getelementptr inbounds %struct.buf_var_size, ptr %bp, i64 0, i32 1 + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %arr, i64 %len.ext + %idx.ext = zext i32 %idx to i64 + %gep = getelementptr i32, ptr %arr, i64 %idx.ext + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_var_size_buffer_gep_inbounds_2(ptr noundef %bp, i32 %idx, i32 %len) { +; CHECK-LABEL: @test_var_size_buffer_gep_inbounds_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds [[STRUCT_BUF_VAR_SIZE:%.*]], ptr [[BP:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[ARR_2:%.*]] = getelementptr [[STRUCT_BUF_VAR_SIZE]], ptr [[BP]], i64 0, i32 1 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ARR_2]], i64 [[IDX_EXT]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %idx, %len + br i1 %cmp, label %then, label %else + +then: + %arr = getelementptr inbounds %struct.buf_var_size, ptr %bp, i64 0, i32 1 + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %arr, i64 %len.ext + %idx.ext = zext i32 %idx to i64 + %arr.2 = getelementptr %struct.buf_var_size, ptr %bp, i64 0, i32 1 + %gep = getelementptr i32, ptr %arr.2, i64 %idx.ext + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_var_size_buffer_gep_missing_inbounds(ptr noundef %bp, i32 %idx, i32 %len) { +; CHECK-LABEL: @test_var_size_buffer_gep_missing_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[ARR:%.*]] = getelementptr [[STRUCT_BUF_VAR_SIZE:%.*]], ptr [[BP:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[C:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[C]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %idx, %len + br i1 %cmp, label %then, label %else + +then: + %arr = getelementptr %struct.buf_var_size, ptr %bp, i64 0, i32 1 + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr i32, ptr %arr, i64 %len.ext + %idx.ext = zext i32 %idx to i64 + %gep = getelementptr i32, ptr %arr, i64 %idx.ext + %c = icmp ult ptr %gep, %add.ptr + ret i1 %c + +else: + ret i1 0 +} + +%struct.T = type { i32, [10 x [20 x i8]] } + +define noundef i1 @test_2_var_size_gep_inbounds_1(ptr noundef %base, i32 %idx.1, i32 %idx.2, i32 %len) { +; CHECK-LABEL: @test_2_var_size_gep_inbounds_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_ULT:%.*]] = icmp ult i32 [[IDX_1:%.*]], 2 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_ULT]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX_2:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[BASE:%.*]], i64 0, i32 1, i64 2, i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = zext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = zext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_T]], ptr [[BASE]], i64 0, i32 1, i64 [[IDX_1_EXT]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[T]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %idx.1.ult = icmp ult i32 %idx.1, 2 + call void @llvm.assume(i1 %idx.1.ult) + %cmp = icmp ult i32 %idx.2, %len + br i1 %cmp, label %then, label %else + +then: + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds %struct.T, ptr %base, i64 0, i32 1, i64 2, i64 %len.ext + %idx.1.ext = zext i32 %idx.1 to i64 + %idx.2.ext = zext i32 %idx.2 to i64 + %gep = getelementptr %struct.T, ptr %base, i64 0, i32 1, i64 %idx.1.ext, i64 %idx.2.ext + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_2_var_size_idx1_may_be_out_of_bounds(ptr noundef %base, i32 %idx.1, i32 %idx.2, i32 %len) { +; CHECK-LABEL: @test_2_var_size_idx1_may_be_out_of_bounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX_2:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[BASE:%.*]], i64 0, i32 1, i64 2, i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = zext i32 [[IDX_1:%.*]] to i64 +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = zext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_T]], ptr [[BASE]], i64 0, i32 1, i64 [[IDX_1_EXT]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[T]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %idx.2, %len + br i1 %cmp, label %then, label %else + +then: + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds %struct.T, ptr %base, i64 0, i32 1, i64 2, i64 %len.ext + %idx.1.ext = zext i32 %idx.1 to i64 + %idx.2.ext = zext i32 %idx.2 to i64 + %gep = getelementptr %struct.T, ptr %base, i64 0, i32 1, i64 %idx.1.ext, i64 %idx.2.ext + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_2_var_size_idx2_may_be_out_of_bounds(ptr noundef %base, i32 %idx.1, i32 %idx.2, i32 %len) { +; CHECK-LABEL: @test_2_var_size_idx2_may_be_out_of_bounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX_1:%.*]], 2 +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[BASE:%.*]], i64 0, i32 1, i64 2, i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = zext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = zext i32 [[IDX_2:%.*]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_T]], ptr [[BASE]], i64 0, i32 1, i64 [[IDX_1_EXT]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[T]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %idx.1, 2 + br i1 %cmp, label %then, label %else + +then: + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds %struct.T, ptr %base, i64 0, i32 1, i64 2, i64 %len.ext + %idx.1.ext = zext i32 %idx.1 to i64 + %idx.2.ext = zext i32 %idx.2 to i64 + %gep = getelementptr %struct.T, ptr %base, i64 0, i32 1, i64 %idx.1.ext, i64 %idx.2.ext + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_upper_bound_2_var_size_gep_inbounds_1(ptr noundef %base, i32 %idx.1, i32 %idx.2, i32 %len.1, i32 %len.2) { +; CHECK-LABEL: @test_upper_bound_2_var_size_gep_inbounds_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_ULT:%.*]] = icmp ugt i32 [[LEN_1:%.*]], 2 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_ULT]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[LEN_2:%.*]], 2 +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_1_EXT:%.*]] = zext i32 [[LEN_1]] to i64 +; CHECK-NEXT: [[LEN_2_EXT:%.*]] = zext i32 [[LEN_2]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[BASE:%.*]], i64 0, i32 1, i64 [[LEN_1_EXT]], i64 [[LEN_2_EXT]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_T]], ptr [[BASE]], i64 0, i32 1, i64 1, i64 1 +; CHECK-NEXT: [[T:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[T]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %idx.1.ult = icmp ugt i32 %len.1, 2 + call void @llvm.assume(i1 %idx.1.ult) + %cmp = icmp ugt i32 %len.2, 2 + br i1 %cmp, label %then, label %else + +then: + %len.1.ext = zext i32 %len.1 to i64 + %len.2.ext = zext i32 %len.2 to i64 + %add.ptr = getelementptr inbounds %struct.T, ptr %base, i64 0, i32 1, i64 %len.1.ext, i64 %len.2.ext + %gep = getelementptr %struct.T, ptr %base, i64 0, i32 1, i64 1, i64 1 + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define noundef i1 @test_upper_bound_2_var_size_gep_may_not_be_inbounds_2(ptr noundef %base, i32 %idx.1, i32 %idx.2, i32 %len.1, i32 %len.2) { +; CHECK-LABEL: @test_upper_bound_2_var_size_gep_may_not_be_inbounds_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[LEN_1:%.*]], 2 +; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_1_EXT:%.*]] = zext i32 [[LEN_1]] to i64 +; CHECK-NEXT: [[LEN_2_EXT:%.*]] = zext i32 [[LEN_2:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[BASE:%.*]], i64 0, i32 1, i64 [[LEN_1_EXT]], i64 [[LEN_2_EXT]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_T]], ptr [[BASE]], i64 0, i32 1, i64 1, i64 1 +; CHECK-NEXT: [[T:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]] +; CHECK-NEXT: ret i1 [[T]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ugt i32 %len.1, 2 + br i1 %cmp, label %then, label %else + +then: + %len.1.ext = zext i32 %len.1 to i64 + %len.2.ext = zext i32 %len.2 to i64 + %add.ptr = getelementptr inbounds %struct.T, ptr %base, i64 0, i32 1, i64 %len.1.ext, i64 %len.2.ext + %gep = getelementptr %struct.T, ptr %base, i64 0, i32 1, i64 1, i64 1 + %t = icmp ult ptr %gep, %add.ptr + ret i1 %t + +else: + ret i1 0 +} + +define i32 @access_struct_1_checks_needed(ptr %src, i32 %size, i32 %idx) { +; CHECK-LABEL: @access_struct_1_checks_needed( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult i32 [[IDX:%.*]], [[SIZE:%.*]] +; CHECK-NEXT: br i1 [[CMP_NOT]], label [[CHECK:%.*]], label [[EXIT_2:%.*]] +; CHECK: check: +; CHECK-NEXT: [[SIZE_EXT:%.*]] = zext i32 [[SIZE]] to i64 +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[SIZE_EXT]] +; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[IDX]], 1 +; CHECK-NEXT: [[ADD_EXT:%.*]] = zext i32 [[ADD]] to i64 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr [[STRUCT_1]], ptr [[SRC]], i64 [[ADD_EXT]] +; CHECK-NEXT: [[GEP_NEXT:%.*]] = getelementptr [[STRUCT_1]], ptr [[GEP_IDX_1]], i64 1 +; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_NEXT]], [[UPPER]] +; CHECK-NEXT: br i1 [[C_1]], label [[CONT:%.*]], label [[EXIT_1:%.*]] +; CHECK: cont: +; CHECK-NEXT: [[C_2:%.*]] = icmp ult ptr [[GEP_IDX_1]], [[UPPER]] +; CHECK-NEXT: br i1 [[C_2]], label [[EXIT_2]], label [[EXIT_1]] +; CHECK: exit.1: +; CHECK-NEXT: ret i32 1 +; CHECK: exit.2: +; CHECK-NEXT: ret i32 3 +; +entry: + %cmp.not = icmp ult i32 %idx, %size + br i1 %cmp.not, label %check, label %exit.2 + +check: + %size.ext = zext i32 %size to i64 + %upper = getelementptr inbounds %struct.1, ptr %src, i64 %size.ext + %add = add nuw i32 %idx, 1 + %add.ext = zext i32 %add to i64 + %gep.idx.1 = getelementptr %struct.1, ptr %src, i64 %add.ext + %gep.next = getelementptr %struct.1, ptr %gep.idx.1, i64 1 + %c.1 = icmp ule ptr %gep.next, %upper + br i1 %c.1, label %cont, label %exit.1 + +cont: + %c.2 = icmp ult ptr %gep.idx.1, %upper + br i1 %c.2, label %exit.2, label %exit.1 + +exit.1: + ret i32 1 + +exit.2: + ret i32 3 +} +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-ub-in-use-blocks.ll b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-ub-in-use-blocks.ll index 7bd2aca816026..2a24a9309ad56 100644 --- a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-ub-in-use-blocks.ll +++ b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition-ub-in-use-blocks.ll @@ -21,8 +21,7 @@ define i1 @inbounds_poison_is_ub_in_use_block_1(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -55,9 +54,8 @@ define i1 @inbounds_poison_is_ub_in_use_block_2(ptr %src, i32 %n, i32 %idx, i1 % ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -96,8 +94,7 @@ define i1 @inbounds_poison_is_ub_in_use_block_by_assume(ptr %src, i32 %n, i32 %i ; CHECK: then: ; CHECK-NEXT: [[CMP_NE:%.*]] = icmp ule ptr null, [[UPPER]] ; CHECK-NEXT: call void @llvm.assume(i1 [[CMP_NE]]) -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -138,8 +135,7 @@ define i1 @inbounds_poison_is_ub_in_in_multiple_use_blocks_1(ptr %src, i32 %n, i ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -279,3 +275,25 @@ else: %cmp.upper.2 = icmp ule ptr %src.idx, %upper ret i1 %cmp.upper.2 } + +define i1 @test_use_in_unreachable_block(i32* nocapture %y) { +; CHECK-LABEL: @test_use_in_unreachable_block( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[Y:%.*]], i64 3 +; CHECK-NEXT: ret i1 false +; CHECK: unreachable.bb: +; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[Y_1:%.*]] = getelementptr inbounds i32, ptr [[Y]], i64 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[Y_1]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %upper = getelementptr inbounds i32, i32* %y, i64 3 + ret i1 false + +unreachable.bb: + call void @noundef(i32* %upper) + %y.1 = getelementptr inbounds i32, i32* %y, i64 1 + %cmp = icmp ule i32* %y.1, %upper + ret i1 %cmp +} diff --git a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition.ll b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition.ll index e22317fb8c794..4ca8cea750994 100644 --- a/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition.ll +++ b/llvm/test/Transforms/ConstraintElimination/geps-inbounds-precondition.ll @@ -13,10 +13,8 @@ define i1 @inbounds_poison_is_ub1(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N:%.*]] ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 ; CHECK-NEXT: [[SRC_IDX_4:%.*]] = getelementptr i32, ptr [[SRC]], i64 4 -; CHECK-NEXT: [[CMP_UPPER_4:%.*]] = icmp ule ptr [[SRC_IDX_4]], [[UPPER]] ; CHECK-NEXT: [[SRC_IDX_5:%.*]] = getelementptr i32, ptr [[SRC]], i64 5 -; CHECK-NEXT: [[CMP_UPPER_5:%.*]] = icmp ule ptr [[SRC_IDX_5]], [[UPPER]] -; CHECK-NEXT: [[RES_0:%.*]] = xor i1 [[CMP_UPPER_4]], [[CMP_UPPER_5]] +; CHECK-NEXT: [[RES_0:%.*]] = xor i1 true, true ; CHECK-NEXT: [[SRC_IDX_6:%.*]] = getelementptr i32, ptr [[SRC]], i64 6 ; CHECK-NEXT: [[CMP_UPPER_6:%.*]] = icmp ule ptr [[SRC_IDX_6]], [[UPPER]] ; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[RES_0]], [[CMP_UPPER_6]] @@ -59,8 +57,7 @@ define i1 @inbounds_poison_is_ub2(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -96,8 +93,7 @@ define i1 @inbounds_poison_is_ub3(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[UPPER_1:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] ; CHECK-NEXT: call void @noundef(ptr [[UPPER_1]]) ; CHECK-NEXT: [[SRC_IDX_1:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX_1]], [[UPPER_1]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[UPPER_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[N_EXT]] ; CHECK-NEXT: call void @noundef(ptr [[UPPER_2]]) @@ -126,6 +122,49 @@ else: ret i1 %cmp.upper.2 } +; Same as inbounds_poison_is_ub3, but with individual upper bound GEPs in the +; %then and %else blocks. +define i1 @inbounds_poison_is_ub4(ptr %src, i8 %n, i8 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_EXT:%.*]] = zext i8 [[N:%.*]] to i16 +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX:%.*]] to i16 +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i16 [[IDX_EXT]], [[N_EXT]] +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[UPPER_1:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i16 [[N_EXT]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER_1]]) +; CHECK-NEXT: [[SRC_IDX_1:%.*]] = getelementptr i32, ptr [[SRC]], i16 [[IDX_EXT]] +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: [[UPPER_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i16 [[N_EXT]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER_2]]) +; CHECK-NEXT: [[SRC_IDX_2:%.*]] = getelementptr i32, ptr [[SRC]], i16 [[IDX_EXT]] +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX_2]], [[UPPER_2]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; +entry: + %n.ext = zext i8 %n to i16 + %idx.ext = zext i8 %idx to i16 + %cmp.idx = icmp ult i16 %idx.ext, %n.ext + br i1 %cmp.idx, label %then, label %else + +then: + %upper.1 = getelementptr inbounds i32, ptr %src, i16 %n.ext + call void @noundef(ptr %upper.1) + %src.idx.1 = getelementptr i32, ptr %src, i16 %idx.ext + %cmp.upper.1 = icmp ule ptr %src.idx.1, %upper.1 + ret i1 %cmp.upper.1 + +else: + %upper.2 = getelementptr inbounds i32, ptr %src, i16 %n.ext + call void @noundef(ptr %upper.2) + %src.idx.2 = getelementptr i32, ptr %src, i16 %idx.ext + %cmp.upper.2 = icmp ule ptr %src.idx.2, %upper.2 + ret i1 %cmp.upper.2 +} + + ; The function does not have UB if %upper is poison because of an overflow. Do ; not simplify anything. In this particular case, the returned result will be ; poison in this case, so it could be simplified, but currently we cannot @@ -247,8 +286,7 @@ define i1 @multiple_upper_bounds(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER_2]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER_2]] ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] @@ -282,8 +320,7 @@ define i1 @multiple_upper_bounds2(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[UPPER_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 4 ; CHECK-NEXT: call void @noundef(ptr [[UPPER_2]]) ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 4 -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER_2]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; entry: %n.ext = zext i32 %n to i64 @@ -305,8 +342,7 @@ define i1 @multiple_upper_bounds3(ptr %src, i32 %n, i32 %idx) { ; CHECK-NEXT: [[UPPER_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 1 ; CHECK-NEXT: call void @noundef(ptr [[UPPER_2]]) ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 4 -; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER_1]] -; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK-NEXT: ret i1 true ; entry: %n.ext = zext i32 %n to i64 @@ -342,3 +378,269 @@ entry: %cmp.upper.1 = icmp ule ptr %src.idx, %upper.2 ret i1 %cmp.upper.1 } + + +; Test case where %n is not trivially signed positive via zext, but via a +; dominating condition. +define i1 @inbounds_poison_is_ub_idx_known_non_negative_via_constraint_system(ptr %src, i32 %n, i32 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub_idx_known_non_negative_via_constraint_system( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_POS:%.*]] = icmp sge i32 [[N:%.*]], 0 +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i32 [[IDX:%.*]], 0 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[N_POS]], [[IDX_POS]] +; CHECK-NEXT: br i1 [[AND]], label [[N_POS_BLOCK:%.*]], label [[EXIT:%.*]] +; CHECK: n.pos.block: +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i32 [[N]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX]], [[N]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; CHECK: exit: +; CHECK-NEXT: ret i1 false +; +entry: + %n.pos = icmp sge i32 %n, 0 + %idx.pos = icmp sge i32 %idx, 0 + %and = and i1 %n.pos, %idx.pos + br i1 %and, label %n.pos.block, label %exit + +n.pos.block: + %upper = getelementptr inbounds i32, ptr %src, i32 %n + call void @noundef(ptr %upper) + %cmp.idx = icmp ult i32 %idx, %n + %idx.ext = zext i32 %idx to i64 + %src.idx = getelementptr i32, ptr %src, i64 %idx.ext + br i1 %cmp.idx, label %then, label %else + +then: + %cmp.upper.1 = icmp ule ptr %src.idx, %upper + ret i1 %cmp.upper.1 + +else: + %cmp.upper.2 = icmp ule ptr %src.idx, %upper + ret i1 %cmp.upper.2 + +exit: + ret i1 0 +} + +define i1 @inbounds_poison_is_ub_idx_not_known_non_negative_via_constraint_system(ptr %src, i32 %n, i32 %idx) { +; CHECK-LABEL: @inbounds_poison_is_ub_idx_not_known_non_negative_via_constraint_system( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 true, label [[N_POS_BLOCK:%.*]], label [[EXIT:%.*]] +; CHECK: n.pos.block: +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i32 [[N:%.*]] +; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) +; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] +; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] +; CHECK: else: +; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] +; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] +; CHECK: exit: +; CHECK-NEXT: ret i1 false +; +entry: + %n.pos = icmp uge i32 %n, 0 + br i1 %n.pos, label %n.pos.block, label %exit + +n.pos.block: + %upper = getelementptr inbounds i32, ptr %src, i32 %n + call void @noundef(ptr %upper) + %cmp.idx = icmp ult i32 %idx, %n + %idx.ext = zext i32 %idx to i64 + %src.idx = getelementptr i32, ptr %src, i64 %idx.ext + br i1 %cmp.idx, label %then, label %else + +then: + %cmp.upper.1 = icmp ule ptr %src.idx, %upper + ret i1 %cmp.upper.1 + +else: + %cmp.upper.2 = icmp ule ptr %src.idx, %upper + ret i1 %cmp.upper.2 + +exit: + ret i1 0 +} + +define i1 @elim_consecutive_writes2(ptr %dst, i32 %n, i32 %idx) { +; CHECK-LABEL: @elim_consecutive_writes2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[IDX:%.*]], 3 +; CHECK-NEXT: br i1 [[CMP]], label [[THEN_1:%.*]], label [[ELSE:%.*]] +; CHECK: then.1: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX]] to i64 +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_IDX]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[GEP_IDX]], [[DST]] +; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[C_1]], [[C_2]] +; CHECK-NEXT: br i1 [[AND_1]], label [[THEN_2:%.*]], label [[ELSE]] +; CHECK: then.2: +; CHECK-NEXT: [[GEP_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 +; CHECK-NEXT: [[AND_2:%.*]] = and i1 true, true +; CHECK-NEXT: ret i1 [[AND_2]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ugt i32 %idx, 3 + br i1 %cmp, label %then.1, label %else + +then.1: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %dst, i64 %idx.ext + %idxprom = zext i32 %idx to i64 + %gep.idx = getelementptr i32, ptr %dst, i64 %idxprom + %c.1 = icmp ult ptr %gep.idx, %add.ptr + %c.2 = icmp uge ptr %gep.idx, %dst + %and.1 = and i1 %c.1, %c.2 + br i1 %and.1, label %then.2, label %else + +then.2: + %gep.4 = getelementptr i32, ptr %dst, i64 4 + %c.3 = icmp ult ptr %gep.4, %add.ptr + %c.4 = icmp uge ptr %gep.4, %dst + %and.2 = and i1 %c.3, %c.4 + ret i1 %and.2 + +else: + ret i1 0 +} + +define void @test_multiple_variable_idx_known_positive_1(ptr noundef %buf, i32 noundef %len, i32 %idx.1, i32 %idx.2) { +; CHECK-LABEL: @test_multiple_variable_idx_known_positive_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_SLT_4:%.*]] = icmp ult i32 [[IDX_1:%.*]], 4 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_SLT_4]]) +; CHECK-NEXT: [[IDX_1_POS:%.*]] = icmp uge i32 [[IDX_1]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_POS]]) +; CHECK-NEXT: [[IDX_2_SLT_3:%.*]] = icmp ult i32 [[IDX_2:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_SLT_3]]) +; CHECK-NEXT: [[IDX_2_POS:%.*]] = icmp uge i32 [[IDX_2]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_POS]]) +; CHECK-NEXT: [[LEN_POS:%.*]] = icmp uge i32 [[LEN:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[LEN_POS]]) +; CHECK-NEXT: [[C_SLT_LEN:%.*]] = icmp ult i32 8, [[LEN]] +; CHECK-NEXT: br i1 [[C_SLT_LEN]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = zext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = zext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_1_EXT]] +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = zext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP_IDX_2:%.*]] = getelementptr i32, ptr [[GEP_IDX_1]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T_1:%.*]] = icmp ult ptr [[GEP_IDX_2]], [[ADD_PTR]] +; CHECK-NEXT: [[T_2:%.*]] = icmp uge ptr [[GEP_IDX_2]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[T_1]], [[T_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.1.slt.4 = icmp ult i32 %idx.1, 4 + call void @llvm.assume(i1 %idx.1.slt.4) + %idx.1.pos = icmp uge i32 %idx.1, 0 + call void @llvm.assume(i1 %idx.1.pos) + %idx.2.slt.3 = icmp ult i32 %idx.2, 3 + call void @llvm.assume(i1 %idx.2.slt.3) + %idx.2.pos = icmp uge i32 %idx.2, 0 + call void @llvm.assume(i1 %idx.2.pos) + %len.pos = icmp uge i32 %len, 0 + call void @llvm.assume(i1 %len.pos) + %c.slt.len = icmp ult i32 8, %len + br i1 %c.slt.len, label %then , label %else + +then: + %len.ext = zext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.1.ext = zext i32 %idx.1 to i64 + %gep.idx.1 = getelementptr i32, ptr %buf, i64 %idx.1.ext + %idx.2.ext = zext i32 %idx.2 to i64 + %gep.idx.2 = getelementptr i32, ptr %gep.idx.1, i64 %idx.2.ext + %t.1 = icmp ult ptr %gep.idx.2, %add.ptr + %t.2 = icmp uge ptr %gep.idx.2, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + +define void @test_multiple_variable_idx_known_positive_2(ptr noundef %buf, i32 noundef %len, i32 %idx.1, i32 %idx.2) { +; CHECK-LABEL: @test_multiple_variable_idx_known_positive_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_1_SLT_4:%.*]] = icmp slt i32 [[IDX_1:%.*]], 4 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_SLT_4]]) +; CHECK-NEXT: [[IDX_1_POS:%.*]] = icmp sge i32 [[IDX_1]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_1_POS]]) +; CHECK-NEXT: [[IDX_2_SLT_3:%.*]] = icmp slt i32 [[IDX_2:%.*]], 3 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_SLT_3]]) +; CHECK-NEXT: [[IDX_2_POS:%.*]] = icmp sge i32 [[IDX_2]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[IDX_2_POS]]) +; CHECK-NEXT: [[LEN_POS:%.*]] = icmp sge i32 [[LEN:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[LEN_POS]]) +; CHECK-NEXT: [[C_SLT_LEN:%.*]] = icmp slt i32 4, [[LEN]] +; CHECK-NEXT: br i1 [[C_SLT_LEN]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[LEN_EXT:%.*]] = sext i32 [[LEN]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[LEN_EXT]] +; CHECK-NEXT: [[IDX_1_EXT:%.*]] = sext i32 [[IDX_1]] to i64 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_1_EXT]] +; CHECK-NEXT: [[IDX_2_EXT:%.*]] = sext i32 [[IDX_2]] to i64 +; CHECK-NEXT: [[GEP_IDX_2:%.*]] = getelementptr i32, ptr [[GEP_IDX_1]], i64 [[IDX_2_EXT]] +; CHECK-NEXT: [[T_1:%.*]] = icmp ult ptr [[GEP_IDX_2]], [[ADD_PTR]] +; CHECK-NEXT: [[T_2:%.*]] = icmp uge ptr [[GEP_IDX_2]], [[BUF]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[T_1]], [[T_2]] +; CHECK-NEXT: call void @use(i1 [[AND]]) +; CHECK-NEXT: ret void +; CHECK: else: +; CHECK-NEXT: ret void +; +entry: + %idx.1.slt.4 = icmp slt i32 %idx.1, 4 + call void @llvm.assume(i1 %idx.1.slt.4) + %idx.1.pos = icmp sge i32 %idx.1, 0 + call void @llvm.assume(i1 %idx.1.pos) + %idx.2.slt.3 = icmp slt i32 %idx.2, 3 + call void @llvm.assume(i1 %idx.2.slt.3) + %idx.2.pos = icmp sge i32 %idx.2, 0 + call void @llvm.assume(i1 %idx.2.pos) + %len.pos = icmp sge i32 %len, 0 + call void @llvm.assume(i1 %len.pos) + %c.slt.len = icmp slt i32 4, %len + br i1 %c.slt.len, label %then , label %else + +then: + %len.ext = sext i32 %len to i64 + %add.ptr = getelementptr inbounds i32, ptr %buf, i64 %len.ext + %idx.1.ext = sext i32 %idx.1 to i64 + %gep.idx.1 = getelementptr i32, ptr %buf, i64 %idx.1.ext + %idx.2.ext = sext i32 %idx.2 to i64 + %gep.idx.2 = getelementptr i32, ptr %gep.idx.1, i64 %idx.2.ext + %t.1 = icmp ult ptr %gep.idx.2, %add.ptr + %t.2 = icmp uge ptr %gep.idx.2, %buf + %and = and i1 %t.1, %t.2 + call void @use(i1 %and) + ret void + +else: + ret void +} + +declare void @use(i1 noundef) +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/geps-precondition-overflow-check.ll b/llvm/test/Transforms/ConstraintElimination/geps-precondition-overflow-check.ll index 08b25c6065aac..4310f8da2c748 100644 --- a/llvm/test/Transforms/ConstraintElimination/geps-precondition-overflow-check.ll +++ b/llvm/test/Transforms/ConstraintElimination/geps-precondition-overflow-check.ll @@ -12,8 +12,7 @@ define i1 @overflow_check_1(ptr %dst) { ; CHECK-NEXT: br i1 [[DST_5_UGE]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 -; CHECK-NEXT: [[TRUE_DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] -; CHECK-NEXT: ret i1 [[TRUE_DST_4_UGE]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: ret i1 false ; @@ -40,8 +39,7 @@ define i1 @overflow_check_2_and(ptr %dst) { ; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 -; CHECK-NEXT: [[TRUE_DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] -; CHECK-NEXT: ret i1 [[TRUE_DST_4_UGE]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: ret i1 true ; @@ -69,8 +67,7 @@ define i1 @overflow_check_3_and(ptr %dst) { ; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 -; CHECK-NEXT: [[DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] -; CHECK-NEXT: ret i1 [[DST_4_UGE]] +; CHECK-NEXT: ret i1 true ; CHECK: else: ; CHECK-NEXT: [[ELSE_DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 ; CHECK-NEXT: [[ELSE_DST_4_UGE:%.*]] = icmp uge ptr [[ELSE_DST_4]], [[DST]] @@ -102,10 +99,8 @@ define i1 @overflow_check_4_and(ptr %dst) { ; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 -; CHECK-NEXT: [[TRUE_DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] ; CHECK-NEXT: [[DST_5_2:%.*]] = getelementptr i32, ptr [[DST]], i64 5 -; CHECK-NEXT: [[TRUE_DST_5_UGE:%.*]] = icmp uge ptr [[DST_5_2]], [[DST]] -; CHECK-NEXT: [[RES_0:%.*]] = xor i1 [[TRUE_DST_4_UGE]], [[TRUE_DST_5_UGE]] +; CHECK-NEXT: [[RES_0:%.*]] = xor i1 true, true ; CHECK-NEXT: [[DST_6:%.*]] = getelementptr i32, ptr [[DST]], i64 6 ; CHECK-NEXT: [[C_DST_6_UGE:%.*]] = icmp uge ptr [[DST_6]], [[DST]] ; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[RES_0]], [[C_DST_6_UGE]] @@ -188,9 +183,7 @@ define i1 @upper_and_lower_checks_1(ptr %dst, i32 %n) { ; CHECK-NEXT: br i1 [[AND_1]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 -; CHECK-NEXT: [[TRUE_DST_4_ULT:%.*]] = icmp ult ptr [[DST_4]], [[UPPER]] -; CHECK-NEXT: [[TRUE_DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[TRUE_DST_4_ULT]], [[TRUE_DST_4_UGE]] +; CHECK-NEXT: [[AND:%.*]] = and i1 true, true ; CHECK-NEXT: ret i1 [[AND]] ; CHECK: else: ; CHECK-NEXT: ret i1 false @@ -304,10 +297,9 @@ define i1 @upper_and_lower_checks_lt(ptr %dst, i32 %n) { ; CHECK-NEXT: br i1 [[OR_COND]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: [[DST_3:%.*]] = getelementptr i32, ptr [[DST]], i64 3 -; CHECK-NEXT: [[TRUE_DST_3_UGE:%.*]] = icmp uge ptr [[DST_3]], [[DST]] ; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 ; CHECK-NEXT: [[C_DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] -; CHECK-NEXT: [[RES_0:%.*]] = xor i1 [[TRUE_DST_3_UGE]], [[C_DST_4_UGE]] +; CHECK-NEXT: [[RES_0:%.*]] = xor i1 true, [[C_DST_4_UGE]] ; CHECK-NEXT: ret i1 [[RES_0]] ; CHECK: else: ; CHECK-NEXT: ret i1 false @@ -331,3 +323,345 @@ then: else: ret i1 0 } + +; The bitcasts in the function are no-ops and %dst.4 can be treated as nuw. +define i1 @overflow_check_with_bitcast_1(i32* %dst) { +; CHECK-LABEL: @overflow_check_with_bitcast_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DST_CAST:%.*]] = bitcast ptr [[DST:%.*]] to ptr +; CHECK-NEXT: [[DST_5:%.*]] = getelementptr i32, ptr [[DST]], i64 5 +; CHECK-NEXT: [[DST_5_CAST:%.*]] = bitcast ptr [[DST_5]] to ptr +; CHECK-NEXT: [[DST_5_UGE:%.*]] = icmp uge ptr [[DST_5_CAST]], [[DST_CAST]] +; CHECK-NEXT: br i1 [[DST_5_UGE]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %dst.cast = bitcast i32* %dst to i8* + %dst.5 = getelementptr i32, i32* %dst, i64 5 + %dst.5.cast = bitcast i32* %dst.5 to i8* + %dst.5.uge = icmp uge i8* %dst.5.cast, %dst.cast + br i1 %dst.5.uge, label %then, label %else + +then: + %dst.4 = getelementptr i32, i32* %dst, i64 4 + %true.dst.4.uge = icmp uge i32* %dst.4, %dst + ret i1 %true.dst.4.uge + +else: + ret i1 0 +} + +; The bitcasts in the function are no-ops, but %dst.5 cannot be treated as nuw, +; because its index (6) exceeds the known-safe one (5). +define i1 @overflow_check_with_bitcast_2(i32* %dst) { +; CHECK-LABEL: @overflow_check_with_bitcast_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DST_CAST:%.*]] = bitcast ptr [[DST:%.*]] to ptr +; CHECK-NEXT: [[DST_5:%.*]] = getelementptr i32, ptr [[DST]], i64 5 +; CHECK-NEXT: [[DST_5_CAST:%.*]] = bitcast ptr [[DST_5]] to ptr +; CHECK-NEXT: [[DST_5_UGE:%.*]] = icmp uge ptr [[DST_5_CAST]], [[DST_CAST]] +; CHECK-NEXT: br i1 [[DST_5_UGE]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[DST_6:%.*]] = getelementptr i32, ptr [[DST]], i64 6 +; CHECK-NEXT: [[DST_6_UGE:%.*]] = icmp uge ptr [[DST_6]], [[DST]] +; CHECK-NEXT: ret i1 [[DST_6_UGE]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %dst.cast = bitcast i32* %dst to i8* + %dst.5 = getelementptr i32, i32* %dst, i64 5 + %dst.5.cast = bitcast i32* %dst.5 to i8* + %dst.5.uge = icmp uge i8* %dst.5.cast, %dst.cast + br i1 %dst.5.uge, label %then, label %else + +then: + %dst.6 = getelementptr i32, i32* %dst, i64 6 + %dst.6.uge = icmp uge i32* %dst.6, %dst + ret i1 %dst.6.uge + +else: + ret i1 0 +} + +; addrspacecast may modify the address value. Do not look through them to +; determine safe bounds for GEPs. +define i1 @overflow_check_with_addrspacecast_1(i32* %dst) { +; CHECK-LABEL: @overflow_check_with_addrspacecast_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DST_CAST:%.*]] = addrspacecast ptr [[DST:%.*]] to ptr addrspace(1) +; CHECK-NEXT: [[DST_5:%.*]] = getelementptr i32, ptr [[DST]], i64 5 +; CHECK-NEXT: [[DST_5_CAST:%.*]] = addrspacecast ptr [[DST_5]] to ptr addrspace(1) +; CHECK-NEXT: [[DST_5_UGE:%.*]] = icmp uge ptr addrspace(1) [[DST_5_CAST]], [[DST_CAST]] +; CHECK-NEXT: br i1 [[DST_5_UGE]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[DST_4:%.*]] = getelementptr i32, ptr [[DST]], i64 4 +; CHECK-NEXT: [[DST_4_UGE:%.*]] = icmp uge ptr [[DST_4]], [[DST]] +; CHECK-NEXT: ret i1 [[DST_4_UGE]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %dst.cast = addrspacecast i32* %dst to i8 addrspace(1) * + %dst.5 = getelementptr i32, i32* %dst, i64 5 + %dst.5.cast = addrspacecast i32* %dst.5 to i8 addrspace(1) * + %dst.5.uge = icmp uge i8 addrspace(1)* %dst.5.cast, %dst.cast + br i1 %dst.5.uge, label %then, label %else + +then: + %dst.4 = getelementptr i32, i32* %dst, i64 4 + %dst.4.uge = icmp uge i32* %dst.4, %dst + ret i1 %dst.4.uge + +else: + ret i1 0 +} + +; addrspacecast may modify the address value. Do not look through them to +; determine safe bounds for GEPs. +define i1 @overflow_check_with_addrspacecast_2(i32* %dst) { +; CHECK-LABEL: @overflow_check_with_addrspacecast_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DST_CAST:%.*]] = addrspacecast ptr [[DST:%.*]] to ptr addrspace(1) +; CHECK-NEXT: [[DST_5:%.*]] = getelementptr i32, ptr [[DST]], i64 5 +; CHECK-NEXT: [[DST_5_CAST:%.*]] = addrspacecast ptr [[DST_5]] to ptr addrspace(1) +; CHECK-NEXT: [[DST_5_UGE:%.*]] = icmp uge ptr addrspace(1) [[DST_5_CAST]], [[DST_CAST]] +; CHECK-NEXT: br i1 [[DST_5_UGE]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[DST_6:%.*]] = getelementptr i32, ptr [[DST]], i64 6 +; CHECK-NEXT: [[DST_6_UGE:%.*]] = icmp uge ptr [[DST_6]], [[DST]] +; CHECK-NEXT: ret i1 [[DST_6_UGE]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %dst.cast = addrspacecast i32* %dst to i8 addrspace(1) * + %dst.5 = getelementptr i32, i32* %dst, i64 5 + %dst.5.cast = addrspacecast i32* %dst.5 to i8 addrspace(1) * + %dst.5.uge = icmp uge i8 addrspace(1)* %dst.5.cast, %dst.cast + br i1 %dst.5.uge, label %then, label %else + +then: + %dst.6 = getelementptr i32, i32* %dst, i64 6 + %dst.6.uge = icmp uge i32* %dst.6, %dst + ret i1 %dst.6.uge + +else: + ret i1 0 +} + + +%struct.1 = type { i32, i64, i8 } + +define i1 @gep_2d_known_inbounds(%struct.1* %start, i32 %idx) { +; CHECK-LABEL: @gep_2d_known_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr [[STRUCT_1:%.*]], ptr [[START:%.*]], i64 6, i32 1 +; CHECK-NEXT: [[ADD_PTR_CAST:%.*]] = bitcast ptr [[ADD_PTR]] to ptr +; CHECK-NEXT: [[START_CAST:%.*]] = bitcast ptr [[START]] to ptr +; CHECK-NEXT: [[C_1:%.*]] = icmp uge ptr [[ADD_PTR_CAST]], [[START_CAST]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_1]], ptr [[START]], i64 4, i32 1 +; CHECK-NEXT: [[START_0_CAST:%.*]] = bitcast ptr [[START_0]] to ptr +; CHECK-NEXT: ret i1 true +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr %struct.1, %struct.1* %start, i64 6, i32 1 + %add.ptr.cast = bitcast i64* %add.ptr to i8* + %start.cast = bitcast %struct.1* %start to i8* + %c.1 = icmp uge i8* %add.ptr.cast, %start.cast + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.1, %struct.1* %start, i64 4, i32 1 + %start.0.cast = bitcast i64* %start.0 to i8* + %c.0 = icmp uge i8* %start.0.cast, %start.cast + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +define i1 @gep_2d_known_first_dimension_not_inbounds(%struct.1* %start, i32 %idx) { +; CHECK-LABEL: @gep_2d_known_first_dimension_not_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr [[STRUCT_1:%.*]], ptr [[START:%.*]], i64 6, i32 1 +; CHECK-NEXT: [[ADD_PTR_CAST:%.*]] = bitcast ptr [[ADD_PTR]] to ptr +; CHECK-NEXT: [[START_CAST:%.*]] = bitcast ptr [[START]] to ptr +; CHECK-NEXT: [[C_1:%.*]] = icmp uge ptr [[ADD_PTR_CAST]], [[START_CAST]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_1]], ptr [[START]], i64 8, i32 1 +; CHECK-NEXT: [[START_0_CAST:%.*]] = bitcast ptr [[START_0]] to ptr +; CHECK-NEXT: [[C_0:%.*]] = icmp uge ptr [[START_0_CAST]], [[START_CAST]] +; CHECK-NEXT: ret i1 [[C_0]] +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr %struct.1, %struct.1* %start, i64 6, i32 1 + %add.ptr.cast = bitcast i64* %add.ptr to i8* + %start.cast = bitcast %struct.1* %start to i8* + %c.1 = icmp uge i8* %add.ptr.cast, %start.cast + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.1, %struct.1* %start, i64 8, i32 1 + %start.0.cast = bitcast i64* %start.0 to i8* + %c.0 = icmp uge i8* %start.0.cast, %start.cast + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +%struct.2 = type { i32, [20 x i64], i8 } + +; TODO: Should keep and use multiple upper bounds. +define i1 @geps_2d_all_dimensions_inbounds(%struct.2* %start, i32 %idx) { +; CHECK-LABEL: @geps_2d_all_dimensions_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[ADD_PTR_CAST:%.*]] = bitcast ptr [[ADD_PTR]] to ptr +; CHECK-NEXT: [[START_CAST:%.*]] = bitcast ptr [[START]] to ptr +; CHECK-NEXT: [[C_1:%.*]] = icmp uge ptr [[ADD_PTR_CAST]], [[START_CAST]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 5, i32 1, i64 4 +; CHECK-NEXT: [[START_0_CAST:%.*]] = bitcast ptr [[START_0]] to ptr +; CHECK-NEXT: ret i1 true +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr %struct.2, %struct.2* %start, i64 6, i32 1, i64 5 + %add.ptr.cast = bitcast i64* %add.ptr to i8* + %start.cast = bitcast %struct.2* %start to i8* + %c.1 = icmp uge i8* %add.ptr.cast, %start.cast + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, %struct.2* %start, i64 5, i32 1, i64 4 + %start.0.cast = bitcast i64* %start.0 to i8* + %c.0 = icmp uge i8* %start.0.cast, %start.cast + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +define i1 @geps_2d_first_dimension_may_not_be_inbounds(%struct.2* %start, i32 %idx) { +; CHECK-LABEL: @geps_2d_first_dimension_may_not_be_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 3 +; CHECK-NEXT: [[ADD_PTR_CAST:%.*]] = bitcast ptr [[ADD_PTR]] to ptr +; CHECK-NEXT: [[START_CAST:%.*]] = bitcast ptr [[START]] to ptr +; CHECK-NEXT: [[C_1:%.*]] = icmp uge ptr [[ADD_PTR_CAST]], [[START_CAST]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[START_0_CAST:%.*]] = bitcast ptr [[START_0]] to ptr +; CHECK-NEXT: [[C_0:%.*]] = icmp uge ptr [[START_0_CAST]], [[START_CAST]] +; CHECK-NEXT: ret i1 [[C_0]] +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr %struct.2, %struct.2* %start, i64 6, i32 1, i64 3 + %add.ptr.cast = bitcast i64* %add.ptr to i8* + %start.cast = bitcast %struct.2* %start to i8* + %c.1 = icmp uge i8* %add.ptr.cast, %start.cast + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, %struct.2* %start, i64 6, i32 1, i64 5 + %start.0.cast = bitcast i64* %start.0 to i8* + %c.0 = icmp uge i8* %start.0.cast, %start.cast + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +define i1 @geps_2d_third_dimension_may_not_be_inbounds(%struct.2* %start, i32 %idx) { +; CHECK-LABEL: @geps_2d_third_dimension_may_not_be_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr [[STRUCT_2:%.*]], ptr [[START:%.*]], i64 6, i32 1, i64 3 +; CHECK-NEXT: [[ADD_PTR_CAST:%.*]] = bitcast ptr [[ADD_PTR]] to ptr +; CHECK-NEXT: [[START_CAST:%.*]] = bitcast ptr [[START]] to ptr +; CHECK-NEXT: [[C_1:%.*]] = icmp uge ptr [[ADD_PTR_CAST]], [[START_CAST]] +; CHECK-NEXT: br i1 [[C_1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX:%.*]] to i64 +; CHECK-NEXT: [[START_0:%.*]] = getelementptr [[STRUCT_2]], ptr [[START]], i64 6, i32 1, i64 5 +; CHECK-NEXT: [[START_0_CAST:%.*]] = bitcast ptr [[START_0]] to ptr +; CHECK-NEXT: [[C_0:%.*]] = icmp uge ptr [[START_0_CAST]], [[START_CAST]] +; CHECK-NEXT: ret i1 [[C_0]] +; CHECK: if.end: +; CHECK-NEXT: ret i1 true +; +entry: + %add.ptr = getelementptr %struct.2, %struct.2* %start, i64 6, i32 1, i64 3 + %add.ptr.cast = bitcast i64* %add.ptr to i8* + %start.cast = bitcast %struct.2* %start to i8* + %c.1 = icmp uge i8* %add.ptr.cast, %start.cast + br i1 %c.1, label %if.then, label %if.end + +if.then: ; preds = %entry + %idx.ext = zext i32 %idx to i64 + %start.0 = getelementptr %struct.2, %struct.2* %start, i64 6, i32 1, i64 5 + %start.0.cast = bitcast i64* %start.0 to i8* + %c.0 = icmp uge i8* %start.0.cast, %start.cast + ret i1 %c.0 + +if.end: ; preds = %entry + ret i1 1 +} + +; The information from the inbounds GEP %upper cannot be used, because %n is +; not known non-negative. Conditions that do not require GEP decomposition to +; simplify are still handled. +define i1 @information_from_inbounds_cannot_be_used_as_offset_is_not_known_non_negative(i32* %dst, i32 %n, i8 %idx, i32* %ptr) { +; CHECK-LABEL: @information_from_inbounds_cannot_be_used_as_offset_is_not_known_non_negative( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i32 [[N:%.*]] +; CHECK-NEXT: call void @noundef(ptr noundef [[UPPER]]) +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr i32, ptr [[DST]], i8 [[IDX:%.*]] +; CHECK-NEXT: [[CMP_1:%.*]] = icmp uge ptr [[GEP_IDX]], [[PTR:%.*]] +; CHECK-NEXT: br i1 [[CMP_1]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %upper = getelementptr inbounds i32, i32* %dst, i32 %n + call void @noundef(i32* noundef %upper) + %gep.idx = getelementptr i32, i32* %dst, i8 %idx + %cmp.1 = icmp uge i32* %gep.idx, %ptr + br i1 %cmp.1, label %then, label %else + +then: + %cmp.2 = icmp uge i32* %gep.idx, %ptr + ret i1 %cmp.2 + +else: + ret i1 0 +} + +declare void @noundef(i32* noundef) diff --git a/llvm/test/Transforms/ConstraintElimination/geps-sext.ll b/llvm/test/Transforms/ConstraintElimination/geps-sext.ll new file mode 100644 index 0000000000000..b9edaec7cc16b --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/geps-sext.ll @@ -0,0 +1,187 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +define i1 @test_gep_sext_known_positive_part_1(i16* readonly %src, i16* readnone %max, i8 %idx) { +; CHECK-LABEL: @test_gep_sext_known_positive_part_1( +; CHECK-NEXT: check.0.min: +; CHECK-NEXT: [[ADD_10:%.*]] = getelementptr inbounds i16, ptr [[SRC:%.*]], i16 10 +; CHECK-NEXT: [[C_ADD_10_MAX:%.*]] = icmp ult ptr [[ADD_10]], [[MAX:%.*]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = sext i8 [[IDX:%.*]] to i16 +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i16 [[IDX_EXT]], 0 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_ADD_10_MAX]], [[IDX_POS]] +; CHECK-NEXT: br i1 [[AND]], label [[CHECK_IDX:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i1 false +; CHECK: check.idx: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[IDX_EXT]], 5 +; CHECK-NEXT: br i1 [[CMP]], label [[CHECK_MAX:%.*]], label [[TRAP]] +; CHECK: check.max: +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_EXT]] +; CHECK-NEXT: [[IDX_1:%.*]] = add nuw nsw i16 [[IDX_EXT]], 1 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_1]] +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, true +; CHECK-NEXT: ret i1 [[R_1]] +; +check.0.min: + %add.10 = getelementptr inbounds i16, i16* %src, i16 10 + %c.add.10.max = icmp ult i16* %add.10, %max + %idx.ext = sext i8 %idx to i16 + %idx.pos = icmp sge i16 %idx.ext, 0 + %and = and i1 %c.add.10.max, %idx.pos + br i1 %and, label %check.idx, label %trap + +trap: + ret i1 0 + +check.idx: + %cmp = icmp ult i16 %idx.ext, 5 + br i1 %cmp, label %check.max, label %trap + +check.max: + %gep.idx = getelementptr inbounds i16, i16* %src, i16 %idx.ext + %c.max.0 = icmp ult i16* %gep.idx, %max + + %idx.1 = add nuw nsw i16 %idx.ext, 1 + %gep.idx.1 = getelementptr inbounds i16, i16* %src, i16 %idx.1 + %c.max.1 = icmp ult i16* %gep.idx.1, %max + + %r.1 = xor i1 %c.max.0, %c.max.1 + ret i1 %r.1 +} + +define i1 @test_gep_sext_known_positive_part_2(i16* readonly %src, i16* readnone %max, i8 %idx) { +; CHECK-LABEL: @test_gep_sext_known_positive_part_2( +; CHECK-NEXT: check.0.min: +; CHECK-NEXT: [[ADD_10:%.*]] = getelementptr inbounds i16, ptr [[SRC:%.*]], i16 10 +; CHECK-NEXT: [[C_ADD_10_MAX:%.*]] = icmp ult ptr [[ADD_10]], [[MAX:%.*]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = sext i8 [[IDX:%.*]] to i16 +; CHECK-NEXT: [[IDX_POS:%.*]] = icmp sge i16 [[IDX_EXT]], 0 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_ADD_10_MAX]], [[IDX_POS]] +; CHECK-NEXT: br i1 [[AND]], label [[CHECK_IDX:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret i1 false +; CHECK: check.idx: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[IDX_EXT]], 5 +; CHECK-NEXT: br i1 [[CMP]], label [[CHECK_MAX:%.*]], label [[TRAP]] +; CHECK: check.max: +; CHECK-NEXT: [[IDX_5:%.*]] = add nuw nsw i16 [[IDX_EXT]], 5 +; CHECK-NEXT: [[GEP_IDX_5:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_5]] +; CHECK-NEXT: [[IDX_6:%.*]] = add nuw nsw i16 [[IDX_EXT]], 6 +; CHECK-NEXT: [[GEP_IDX_6:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_6]] +; CHECK-NEXT: [[R_3:%.*]] = xor i1 true, true +; CHECK-NEXT: [[IDX_10:%.*]] = add nuw nsw i16 [[IDX_EXT]], 10 +; CHECK-NEXT: [[GEP_IDX_10:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_10]] +; CHECK-NEXT: [[C_MAX_10:%.*]] = icmp ult ptr [[GEP_IDX_10]], [[MAX]] +; CHECK-NEXT: [[R_4:%.*]] = xor i1 [[R_3]], [[C_MAX_10]] +; CHECK-NEXT: ret i1 [[R_4]] +; +check.0.min: + %add.10 = getelementptr inbounds i16, i16* %src, i16 10 + %c.add.10.max = icmp ult i16* %add.10, %max + %idx.ext = sext i8 %idx to i16 + %idx.pos = icmp sge i16 %idx.ext, 0 + %and = and i1 %c.add.10.max, %idx.pos + br i1 %and, label %check.idx, label %trap + +trap: + ret i1 0 + +check.idx: + %cmp = icmp ult i16 %idx.ext, 5 + br i1 %cmp, label %check.max, label %trap + +check.max: + %idx.5 = add nuw nsw i16 %idx.ext, 5 + %gep.idx.5 = getelementptr inbounds i16, i16* %src, i16 %idx.5 + %c.max.5 = icmp ult i16* %gep.idx.5, %max + + %idx.6 = add nuw nsw i16 %idx.ext, 6 + %gep.idx.6 = getelementptr inbounds i16, i16* %src, i16 %idx.6 + %c.max.6 = icmp ult i16* %gep.idx.6, %max + + %r.3 = xor i1 %c.max.5, %c.max.6 + + %idx.10 = add nuw nsw i16 %idx.ext, 10 + %gep.idx.10 = getelementptr inbounds i16, i16* %src, i16 %idx.10 + %c.max.10 = icmp ult i16* %gep.idx.10, %max + + %r.4 = xor i1 %r.3, %c.max.10 + ret i1 %r.4 +} + +define void @test_gep_sext_not_known_positive(i16* readonly %src, i16* readnone %max, i8 %idx) { +; CHECK-LABEL: @test_gep_sext_not_known_positive( +; CHECK-NEXT: check.0.min: +; CHECK-NEXT: [[ADD_10:%.*]] = getelementptr inbounds i16, ptr [[SRC:%.*]], i16 10 +; CHECK-NEXT: [[C_ADD_10_MAX:%.*]] = icmp ult ptr [[ADD_10]], [[MAX:%.*]] +; CHECK-NEXT: [[IDX_EXT:%.*]] = sext i8 [[IDX:%.*]] to i16 +; CHECK-NEXT: br i1 [[C_ADD_10_MAX]], label [[CHECK_IDX:%.*]], label [[TRAP:%.*]] +; CHECK: trap: +; CHECK-NEXT: ret void +; CHECK: check.idx: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[IDX_EXT]], 5 +; CHECK-NEXT: br i1 [[CMP]], label [[CHECK_MAX:%.*]], label [[TRAP]] +; CHECK: check.max: +; CHECK-NEXT: [[GEP_IDX:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_EXT]] +; CHECK-NEXT: [[C_MAX_0:%.*]] = icmp ult ptr [[GEP_IDX]], [[MAX]] +; CHECK-NEXT: call void @use(i1 [[C_MAX_0]]) +; CHECK-NEXT: [[IDX_1:%.*]] = add nuw nsw i16 [[IDX_EXT]], 1 +; CHECK-NEXT: [[GEP_IDX_1:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_1]] +; CHECK-NEXT: [[C_MAX_1:%.*]] = icmp ult ptr [[GEP_IDX_1]], [[MAX]] +; CHECK-NEXT: call void @use(i1 [[C_MAX_1]]) +; CHECK-NEXT: [[IDX_5:%.*]] = add nuw nsw i16 [[IDX_EXT]], 5 +; CHECK-NEXT: [[GEP_IDX_5:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_5]] +; CHECK-NEXT: [[C_MAX_5:%.*]] = icmp ult ptr [[GEP_IDX_5]], [[MAX]] +; CHECK-NEXT: call void @use(i1 [[C_MAX_5]]) +; CHECK-NEXT: [[IDX_6:%.*]] = add nuw nsw i16 [[IDX_EXT]], 6 +; CHECK-NEXT: [[GEP_IDX_6:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_6]] +; CHECK-NEXT: [[C_MAX_6:%.*]] = icmp ult ptr [[GEP_IDX_6]], [[MAX]] +; CHECK-NEXT: call void @use(i1 [[C_MAX_6]]) +; CHECK-NEXT: [[IDX_10:%.*]] = add nuw nsw i16 [[IDX_EXT]], 10 +; CHECK-NEXT: [[GEP_IDX_10:%.*]] = getelementptr inbounds i16, ptr [[SRC]], i16 [[IDX_10]] +; CHECK-NEXT: [[C_MAX_10:%.*]] = icmp ult ptr [[GEP_IDX_10]], [[MAX]] +; CHECK-NEXT: call void @use(i1 [[C_MAX_10]]) +; CHECK-NEXT: ret void +; +check.0.min: + %add.10 = getelementptr inbounds i16, i16* %src, i16 10 + %c.add.10.max = icmp ult i16* %add.10, %max + %idx.ext = sext i8 %idx to i16 + br i1 %c.add.10.max, label %check.idx, label %trap + +trap: + ret void + +check.idx: + %cmp = icmp ult i16 %idx.ext, 5 + br i1 %cmp, label %check.max, label %trap + +check.max: + %gep.idx = getelementptr inbounds i16, i16* %src, i16 %idx.ext + %c.max.0 = icmp ult i16* %gep.idx, %max + call void @use(i1 %c.max.0) + + %idx.1 = add nuw nsw i16 %idx.ext, 1 + %gep.idx.1 = getelementptr inbounds i16, i16* %src, i16 %idx.1 + %c.max.1 = icmp ult i16* %gep.idx.1, %max + call void @use(i1 %c.max.1) + + %idx.5 = add nuw nsw i16 %idx.ext, 5 + %gep.idx.5 = getelementptr inbounds i16, i16* %src, i16 %idx.5 + %c.max.5 = icmp ult i16* %gep.idx.5, %max + call void @use(i1 %c.max.5) + + %idx.6 = add nuw nsw i16 %idx.ext, 6 + %gep.idx.6 = getelementptr inbounds i16, i16* %src, i16 %idx.6 + %c.max.6 = icmp ult i16* %gep.idx.6, %max + call void @use(i1 %c.max.6) + + %idx.10 = add nuw nsw i16 %idx.ext, 10 + %gep.idx.10 = getelementptr inbounds i16, i16* %src, i16 %idx.10 + %c.max.10 = icmp ult i16* %gep.idx.10, %max + call void @use(i1 %c.max.10) + + ret void +} + +declare void @use(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll index a80e492e08246..53629d82bc52a 100644 --- a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll +++ b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll @@ -1,6 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + declare void @use(i1) define void @test_unsigned_too_large(i128 %x) { diff --git a/llvm/test/Transforms/ConstraintElimination/large-system-growth.ll b/llvm/test/Transforms/ConstraintElimination/large-system-growth.ll index 147bc4210d557..bfdcccd84f29b 100644 --- a/llvm/test/Transforms/ConstraintElimination/large-system-growth.ll +++ b/llvm/test/Transforms/ConstraintElimination/large-system-growth.ll @@ -15,13 +15,11 @@ define void @test(i64 %x, ptr %y, ptr %z, ptr %w) { ; CHECK-NEXT: [[TMP30:%.*]] = icmp ult ptr [[TMP29]], [[Z]] ; CHECK-NEXT: br i1 [[TMP30]], label [[EARLY_EXIT]], label [[BB32:%.*]] ; CHECK: bb32: -; CHECK-NEXT: [[TMP33:%.*]] = icmp ult ptr [[TMP29]], [[Z]] -; CHECK-NEXT: br i1 [[TMP33]], label [[BB35:%.*]], label [[EARLY_EXIT]] +; CHECK-NEXT: br i1 false, label [[BB35:%.*]], label [[EARLY_EXIT]] ; CHECK: bb35: -; CHECK-NEXT: [[TMP36:%.*]] = icmp ult ptr [[Y]], [[Z]] -; CHECK-NEXT: br i1 [[TMP36]], label [[EARLY_EXIT]], label [[BB38:%.*]] +; CHECK-NEXT: br i1 true, label [[EARLY_EXIT]], label [[BB38:%.*]] ; CHECK: bb38: -; CHECK-NEXT: br i1 false, label [[EARLY_EXIT]], label [[BB43:%.*]] +; CHECK-NEXT: br i1 true, label [[EARLY_EXIT]], label [[BB43:%.*]] ; CHECK: bb43: ; CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds i8, ptr [[W:%.*]], i64 [[X]] ; CHECK-NEXT: [[TMP48:%.*]] = icmp ult ptr [[TMP47]], [[Y]] @@ -31,8 +29,7 @@ define void @test(i64 %x, ptr %y, ptr %z, ptr %w) { ; CHECK-NEXT: [[TMP53:%.*]] = icmp ult ptr [[TMP52]], [[Y]] ; CHECK-NEXT: br i1 [[TMP53]], label [[EARLY_EXIT]], label [[BB55:%.*]] ; CHECK: bb55: -; CHECK-NEXT: [[TMP57:%.*]] = icmp ult ptr [[W]], [[Y]] -; CHECK-NEXT: br i1 [[TMP57]], label [[BB59:%.*]], label [[EARLY_EXIT]] +; CHECK-NEXT: br i1 true, label [[BB59:%.*]], label [[EARLY_EXIT]] ; CHECK: bb59: ; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/ConstraintElimination/loop-max-backedge-taken-count.ll b/llvm/test/Transforms/ConstraintElimination/loop-max-backedge-taken-count.ll new file mode 100644 index 0000000000000..9bf819708962e --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/loop-max-backedge-taken-count.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 +; RUN: opt -p constraint-elimination -S %s | FileCheck %s + +declare zeroext i8 @foo() + +define i1 @test(ptr %arg) { +; CHECK-LABEL: define i1 @test( +; CHECK-SAME: ptr [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] +; CHECK: loop.header: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] +; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[ARG]], align 8 +; CHECK-NEXT: [[EC:%.*]] = icmp ugt i32 [[IV]], [[L]] +; CHECK-NEXT: br i1 [[EC]], label [[EXIT:%.*]], label [[LOOP_LATCH]] +; CHECK: loop.latch: +; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; CHECK-NEXT: br label [[LOOP_HEADER]] +; CHECK: exit: +; CHECK-NEXT: [[CALL:%.*]] = call zeroext i8 @foo() +; CHECK-NEXT: [[C_1:%.*]] = icmp ult i8 [[CALL]], 2 +; CHECK-NEXT: br i1 [[C_1]], label [[RET_1:%.*]], label [[RET_2:%.*]] +; CHECK: ret.1: +; CHECK-NEXT: [[C_2:%.*]] = icmp eq i8 [[CALL]], 0 +; CHECK-NEXT: ret i1 [[C_2]] +; CHECK: ret.2: +; CHECK-NEXT: ret i1 false +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ 1, %entry ], [ %iv.next, %loop.latch ] + %l = load i32, ptr %arg, align 8 + %ec = icmp ugt i32 %iv, %l + br i1 %ec, label %exit, label %loop.latch + +loop.latch: + %iv.next = add nuw nsw i32 %iv, 1 + br label %loop.header + +exit: + %call = call zeroext i8 @foo() + %c.1 = icmp ult i8 %call, 2 + br i1 %c.1, label %ret.1, label %ret.2 + +ret.1: + %c.2 = icmp eq i8 %call, 0 + ret i1 %c.2 + +ret.2: + ret i1 false +} diff --git a/llvm/test/Transforms/ConstraintElimination/loop-with-unrelated-check.ll b/llvm/test/Transforms/ConstraintElimination/loop-with-unrelated-check.ll new file mode 100644 index 0000000000000..5f8b8ac6eaae5 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/loop-with-unrelated-check.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -p constraint-elimination -S %s | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32" + +declare void @foo(i32) + +define i1 @test(ptr %p1, ptr %p2) { +; CHECK-LABEL: define i1 @test( +; CHECK-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[LOOP_HEADER:.*]] +; CHECK: [[LOOP_HEADER]]: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ] +; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[P1]], align 1 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[IV]], [[L]] +; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[LOOP_LATCH]] +; CHECK: [[LOOP_LATCH]]: +; CHECK-NEXT: call void @foo(i32 [[IV]]) +; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1 +; CHECK-NEXT: br label %[[LOOP_HEADER]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[P2]], null +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ] + %l = load i32, ptr %p1, align 1 + %c = icmp ugt i32 %iv, %l + br i1 %c, label %exit, label %loop.latch + +loop.latch: + call void @foo(i32 %iv) + %iv.next = add nuw i32 %iv, 1 + br label %loop.header + +exit: + %res = icmp ne ptr %p2, null + ret i1 %res +} diff --git a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll index 3dbea9496da8d..9a21300041cce 100644 --- a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll @@ -9,13 +9,10 @@ define void @loop_iv_cond_variable_bound(i32 %n) { ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[IV]], [[N:%.*]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp sge i32 [[IV]], 0 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[T_3:%.*]] = icmp sge i32 [[IV]], -1 -; CHECK-NEXT: call void @use(i1 [[T_3]]) -; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], [[N]] +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], [[N:%.*]] ; CHECK-NEXT: call void @use(i1 [[C_1]]) ; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[IV]], 1 ; CHECK-NEXT: call void @use(i1 [[C_2]]) @@ -56,12 +53,9 @@ define void @loop_iv_cond_constant_bound() { ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[IV]], 2 -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp sge i32 [[IV]], 0 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[T_3:%.*]] = icmp sge i32 [[IV]], -1 -; CHECK-NEXT: call void @use(i1 [[T_3]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], 2 ; CHECK-NEXT: call void @use(i1 [[C_1]]) ; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[IV]], 1 diff --git a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-pointer-cmps.ll b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-pointer-cmps.ll index 279238bea1842..b003553ff51b2 100644 --- a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-pointer-cmps.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-pointer-cmps.ll @@ -21,7 +21,6 @@ define void @checks_in_loops_removable(ptr %ptr, ptr %lower, ptr %upper, i8 %n) ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i16 [ 0, [[PRE_2]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] ; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i16 [[IV]] -; CHECK-NEXT: [[CMP_PTR_IV_LOWER:%.*]] = icmp ugt ptr [[LOWER]], [[PTR_IV]] ; CHECK-NEXT: [[CMP_PTR_IV_UPPER:%.*]] = icmp ule ptr [[UPPER]], [[PTR_IV]] ; CHECK-NEXT: [[OR:%.*]] = or i1 false, [[CMP_PTR_IV_UPPER]] ; CHECK-NEXT: br i1 [[OR]], label [[TRAP]], label [[LOOP_LATCH]] @@ -86,7 +85,6 @@ define void @some_checks_in_loops_removable(ptr %ptr, ptr %lower, ptr %upper, i8 ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i16 [ 0, [[PRE_2]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] ; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i16 [[IV]] -; CHECK-NEXT: [[CMP_PTR_IV_LOWER:%.*]] = icmp ugt ptr [[LOWER]], [[PTR_IV]] ; CHECK-NEXT: [[CMP_PTR_IV_UPPER:%.*]] = icmp ule ptr [[UPPER]], [[PTR_IV]] ; CHECK-NEXT: [[OR:%.*]] = or i1 false, [[CMP_PTR_IV_UPPER]] ; CHECK-NEXT: br i1 [[OR]], label [[TRAP]], label [[LOOP_BODY:%.*]] @@ -163,7 +161,6 @@ define void @no_checks_in_loops_removable(ptr %ptr, ptr %lower, ptr %upper, i8 % ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i16 [ 0, [[PRE_1]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] ; CHECK-NEXT: [[PTR_IV:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i16 [[IV]] -; CHECK-NEXT: [[CMP_PTR_IV_LOWER:%.*]] = icmp ugt ptr [[LOWER]], [[PTR_IV]] ; CHECK-NEXT: [[CMP_PTR_IV_UPPER:%.*]] = icmp ule ptr [[UPPER]], [[PTR_IV]] ; CHECK-NEXT: [[OR:%.*]] = or i1 false, [[CMP_PTR_IV_UPPER]] ; CHECK-NEXT: br i1 [[OR]], label [[TRAP]], label [[LOOP_BODY:%.*]] diff --git a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll index 7b928a030614b..59c8b08e12aeb 100644 --- a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll @@ -248,12 +248,11 @@ define void @loop_latch_not_executed_constant_bound(i32 %y, i1 %c) { ; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP_HEADER:%.*]], label [[EXIT:%.*]] ; CHECK: loop.header: ; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] -; CHECK-NEXT: [[C_1:%.*]] = icmp ugt i32 [[X]], 10 -; CHECK-NEXT: br i1 [[C_1]], label [[LOOP_LATCH]], label [[EXIT]] +; CHECK-NEXT: br i1 false, label [[LOOP_LATCH]], label [[EXIT]] ; CHECK: loop.latch: -; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[X_NEXT]] = add i32 [[X]], 1 ; CHECK-NEXT: br label [[LOOP_HEADER]] @@ -297,13 +296,10 @@ define void @loop_iv_cond_variable_bound(i32 %n) { ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] -; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[IV]], [[N:%.*]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp sge i32 [[IV]], 0 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[T_3:%.*]] = icmp sge i32 [[IV]], -1 -; CHECK-NEXT: call void @use(i1 [[T_3]]) -; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], [[N]] +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], [[N:%.*]] ; CHECK-NEXT: call void @use(i1 [[C_1]]) ; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[IV]], 1 ; CHECK-NEXT: call void @use(i1 [[C_2]]) @@ -362,12 +358,9 @@ define void @loop_iv_cond_constant_bound() { ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] -; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[IV]], 2 -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp sge i32 [[IV]], 0 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[T_3:%.*]] = icmp sge i32 [[IV]], -1 -; CHECK-NEXT: call void @use(i1 [[T_3]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[C_1:%.*]] = icmp ult i32 [[IV]], 2 ; CHECK-NEXT: call void @use(i1 [[C_1]]) ; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[IV]], 1 diff --git a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-pointer-cmps.ll b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-pointer-cmps.ll index 1842ca2d82545..57191a8ebc087 100644 --- a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-pointer-cmps.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-pointer-cmps.ll @@ -210,7 +210,6 @@ define void @test2_with_ne(ptr %src, ptr %lower, ptr %upper, i8 %N) { ; CHECK-NEXT: br i1 [[EC]], label [[EXIT:%.*]], label [[LOOP_BODY:%.*]] ; CHECK: loop.body: ; CHECK-NEXT: [[SRC_IV:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i8 [[IV]] -; CHECK-NEXT: [[CMP_IV_START:%.*]] = icmp ult ptr [[SRC_IV]], [[LOWER]] ; CHECK-NEXT: [[CMP_IV_END:%.*]] = icmp uge ptr [[SRC_IV]], [[UPPER]] ; CHECK-NEXT: [[OR_1:%.*]] = or i1 false, [[CMP_IV_END]] ; CHECK-NEXT: br i1 [[OR_1]], label [[TRAP_BB]], label [[LOOP_BODY_1:%.*]] @@ -304,7 +303,6 @@ define void @test3(ptr %src, ptr %lower, ptr %upper, i8 %N) { ; CHECK-NEXT: br i1 [[EC]], label [[LOOP_BODY:%.*]], label [[EXIT:%.*]] ; CHECK: loop.body: ; CHECK-NEXT: [[SRC_IV:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i8 [[IV]] -; CHECK-NEXT: [[CMP_IV_START:%.*]] = icmp ult ptr [[SRC_IV]], [[LOWER]] ; CHECK-NEXT: [[CMP_IV_END:%.*]] = icmp uge ptr [[SRC_IV]], [[UPPER]] ; CHECK-NEXT: [[OR_1:%.*]] = or i1 false, [[CMP_IV_END]] ; CHECK-NEXT: br i1 [[OR_1]], label [[TRAP_BB]], label [[LOOP_BODY_1:%.*]] diff --git a/llvm/test/Transforms/ConstraintElimination/loops.ll b/llvm/test/Transforms/ConstraintElimination/loops.ll index 25d4ac436ce8c..6034b82e0a34d 100644 --- a/llvm/test/Transforms/ConstraintElimination/loops.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops.ll @@ -107,15 +107,12 @@ define i32 @loop_header_dom_successors_flipped(i32 %y, i1 %c) { ; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP_HEADER:%.*]], label [[EXIT:%.*]] ; CHECK: loop.header: ; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] -; CHECK-NEXT: [[C_1:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: br i1 [[C_1]], label [[EXIT]], label [[LOOP_LATCH]] +; CHECK-NEXT: br i1 true, label [[EXIT]], label [[LOOP_LATCH]] ; CHECK: loop.latch: -; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[X]], 11 -; CHECK-NEXT: call void @use(i1 [[C_2]]) -; CHECK-NEXT: [[C_3:%.*]] = icmp ule i32 [[X]], 11 -; CHECK-NEXT: call void @use(i1 [[C_3]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[X_NEXT]] = add i32 [[X]], 1 ; CHECK-NEXT: br label [[LOOP_HEADER]] ; CHECK: exit: @@ -456,3 +453,35 @@ exit.1: call void @use(i1 %t.1) ret void } + + +define i1 @no_predecessor_loop(i32 %start_val) { +; CHECK-LABEL: @no_predecessor_loop( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 0, [[START_VAL:%.*]] +; CHECK-NEXT: br label [[FOR_COND10:%.*]] +; CHECK: for.cond10: +; CHECK-NEXT: br label [[FOR_COND10]] +; CHECK: if.then87: +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: for.cond159: +; CHECK-NEXT: br label [[FOR_COND159:%.*]] +; CHECK: for.cond240: +; CHECK-NEXT: br label [[FOR_COND240:%.*]] +; +entry: + %cmp = icmp ne i32 0, %start_val + br label %for.cond10 + +for.cond10: ; preds = %for.cond10, %entry + br label %for.cond10 + +if.then87: ; No predecessors! + ret i1 %cmp + +for.cond159: ; preds = %for.cond159 + br label %for.cond159 + +for.cond240: ; preds = %for.cond240 + br label %for.cond240 +} diff --git a/llvm/test/Transforms/ConstraintElimination/loops_induction.ll b/llvm/test/Transforms/ConstraintElimination/loops_induction.ll new file mode 100644 index 0000000000000..2aa7abcd74ca8 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/loops_induction.ll @@ -0,0 +1,953 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +declare void @use(i1) +define void @loop_gep_phi(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_two_induction_vars(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_two_induction_vars( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I2_0:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[INC]], [[FOR_BODY]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i2.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void + +} + +define void @loop_gep_phi_two_induction_vars_2(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_two_induction_vars_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I2_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[INC]], [[FOR_BODY]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i2.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_inc_by_2(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_inc_by_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 2 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 2 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 2 + %inc = add nuw nsw i32 %i.0, 2 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_two_gep_phis(ptr %a, ptr %b, i32 %n) { +; CHECK-LABEL: @loop_two_gep_phis( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i32, ptr [[B:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[B]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[PTR1_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR1_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[PTR1_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR1_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + %add.ptr2 = getelementptr inbounds i32, ptr %b, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %b, %entry ], [ %ptr.iv.next, %for.body ] + %ptr1.iv = phi ptr [ %a, %entry ], [ %ptr1.iv.next, %for.body] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr1.iv, %add.ptr + %c.2 = icmp uge ptr %ptr1.iv, %a + %c.3 = icmp ult ptr %ptr.iv, %add.ptr2 + %c.4 = icmp uge ptr %ptr.iv, %b + call void @use(i1 %c.1) + call void @use(i1 %c.2) + call void @use(i1 %c.3) + call void @use(i1 %c.4) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %ptr1.iv.next = getelementptr inbounds i32, ptr %ptr1.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_induction_var_incorrect_step(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_induction_var_incorrect_step( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 4 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 4 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @start_val_non_zero(ptr %a, i32 %n) { +; CHECK-LABEL: @start_val_non_zero( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 1, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + + +define void @loop_phi_ptr_not_inductive(ptr %a, ptr %base2, i32 %n) { +; CHECK-LABEL: @loop_phi_ptr_not_inductive( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[BASE2:%.*]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 1, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %base2, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_induction_switched(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_induction_switched( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[INC:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @induction_two_loops(ptr %a, ptr %b, i32 %n, i32 %n2) { +; CHECK-LABEL: @induction_two_loops( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: [[IDX_EXT1:%.*]] = zext i32 [[N2:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i32, ptr [[B:%.*]], i64 [[IDX_EXT1]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_PRED:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.pred: +; CHECK-NEXT: br label [[FOR_COND2:%.*]] +; CHECK: for.cond2: +; CHECK-NEXT: [[I2_0:%.*]] = phi i32 [ 0, [[FOR_PRED]] ], [ [[INC21:%.*]], [[FOR_BODY2:%.*]] ] +; CHECK-NEXT: [[PTR1_IV:%.*]] = phi ptr [ [[B]], [[FOR_PRED]] ], [ [[PTR1_IV_NEXT:%.*]], [[FOR_BODY2]] ] +; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[I2_0]], [[N2]] +; CHECK-NEXT: br i1 [[CMP2]], label [[FOR_BODY2]], label [[FOR_COND_CLEANUP6:%.*]] +; CHECK: for.body2: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR1_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR1_IV]], i64 1 +; CHECK-NEXT: [[INC21]] = add nuw nsw i32 [[I2_0]], 1 +; CHECK-NEXT: br label [[FOR_COND2]] +; CHECK: for.cond.cleanup6: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + %idx.ext1 = zext i32 %n2 to i64 + %add.ptr2 = getelementptr inbounds i32, ptr %b, i64 %idx.ext1 + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.pred + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.pred: + br label %for.cond2 + +for.cond2: + %i2.0 = phi i32 [ 0, %for.pred ], [ %inc21, %for.body2 ] + %ptr1.iv = phi ptr [ %b, %for.pred ], [ %ptr1.iv.next, %for.body2 ] + %cmp2 = icmp ult i32 %i2.0, %n2 + br i1 %cmp2, label %for.body2, label %for.cond.cleanup6 + +for.body2: + %c.3 = icmp ult ptr %ptr1.iv, %add.ptr2 + %c.4 = icmp uge ptr %ptr1.iv, %b + call void @use(i1 %c.3) + call void @use(i1 %c.4) + %ptr1.iv.next = getelementptr inbounds i32, ptr %ptr1.iv, i64 1 + %inc21 = add nuw nsw i32 %i2.0, 1 + br label %for.cond2 + +for.cond.cleanup6: + ret void +} + +define void @loop_gep_phi_incoming_value_switched(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_incoming_value_switched( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ], [ [[A]], [[ENTRY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %ptr.iv.next, %for.body ], [ %a, %entry ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_different_types_cmp(i8* %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_different_types_cmp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i128, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, i8* %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi i8* [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult i8* %ptr.iv, %add.ptr + %c.2 = icmp uge i8* %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i128, i8* %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_different_types_cmp2(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_different_types_cmp2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i8, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i8, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @phi_ptr_multi_latch(ptr %a, i32 %n) { +; CHECK-LABEL: @phi_ptr_multi_latch( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ], [ [[INC2:%.*]], [[LATCH:%.*]] ] +; CHECK-NEXT: [[R_0:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[FOR_BODY]] ], [ [[ADD_2:%.*]], [[LATCH]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ], [ [[PTR_IV_NEXT2:%.*]], [[LATCH]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[LATCH]] +; CHECK: latch: +; CHECK-NEXT: [[PTR_IV_NEXT2]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 -1 +; CHECK-NEXT: [[LOAD_2:%.*]] = load i32, ptr [[PTR_IV]], align 4 +; CHECK-NEXT: [[ADD_2]] = add nsw i32 [[R_0]], [[LOAD_2]] +; CHECK-NEXT: [[INC2]] = add nuw nsw i32 [[I_0]], -1 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[R_0]], 10 +; CHECK-NEXT: br i1 [[CMP2]], label [[FOR_COND]], label [[END:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR_IV]], align 4 +; CHECK-NEXT: [[ADD]] = add nsw i32 [[R_0]], [[TMP0]] +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: end: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ], [%inc2, %latch] + %r.0 = phi i32 [ 0, %entry ], [ %add, %for.body ], [%add.2, %latch] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ], [%ptr.iv.next2, %latch] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %latch + +latch: + %ptr.iv.next2 = getelementptr inbounds i32, ptr %ptr.iv, i64 -1 + %load.2 = load i32, ptr %ptr.iv, align 4 + %add.2 = add nsw i32 %r.0, %load.2 + %inc2 = add nuw nsw i32 %i.0, -1 + %cmp2 = icmp ule i32 %r.0, 10 + br i1 %cmp2, label %for.cond, label %end + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %0 = load i32, ptr %ptr.iv, align 4 + %add = add nsw i32 %r.0, %0 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +end: +ret void +} + +define void @loop_gep_phi_vector(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_vector( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds <2 x i8>, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi i8* [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds <2 x i8>, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_vector_2(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_vector_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds <4 x i32>, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi i8* [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds <4 x i32>, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_vector_scalable(ptr %a, i32 %n) { +; CHECK-LABEL: @loop_gep_phi_vector_scalable( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds , ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi i8* [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds , ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_non_const_int_iv_increment(ptr %a, i32 %n, i32 %increment) { +; CHECK-LABEL: @loop_gep_phi_non_const_int_iv_increment( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], [[INCREMENT:%.*]] +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i64 1 + %inc = add nuw nsw i32 %i.0, %increment + br label %for.cond + +for.cond.cleanup: + ret void +} + +define void @loop_gep_phi_non_const_ptr_iv_increment(ptr %a, i32 %n, i32 %increment) { +; CHECK-LABEL: @loop_gep_phi_non_const_ptr_iv_increment( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +; CHECK-NEXT: br label [[FOR_COND:%.*]] +; CHECK: for.cond: +; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY:%.*]] ] +; CHECK-NEXT: [[PTR_IV:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[PTR_IV_NEXT:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[PTR_IV]], [[ADD_PTR]] +; CHECK-NEXT: [[C_2:%.*]] = icmp uge ptr [[PTR_IV]], [[A]] +; CHECK-NEXT: call void @use(i1 [[C_1]]) +; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: [[PTR_IV_NEXT]] = getelementptr inbounds i32, ptr [[PTR_IV]], i32 [[INCREMENT:%.*]] +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: br label [[FOR_COND]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; +entry: + %idx.ext = zext i32 %n to i64 + %add.ptr = getelementptr inbounds i32, ptr %a, i64 %idx.ext + br label %for.cond + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.iv = phi ptr [ %a, %entry ], [ %ptr.iv.next, %for.body ] + %cmp = icmp ult i32 %i.0, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: + %c.1 = icmp ult ptr %ptr.iv, %add.ptr + %c.2 = icmp uge ptr %ptr.iv, %a + call void @use(i1 %c.1) + call void @use(i1 %c.2) + %ptr.iv.next = getelementptr inbounds i32, ptr %ptr.iv, i32 %increment + %inc = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.cond.cleanup: + ret void +} + +define i1 @loop_gep_phi_non_const_induction_ptr(i64 %arg, ptr %arg1) { +; CHECK-LABEL: @loop_gep_phi_non_const_induction_ptr( +; CHECK-NEXT: bb: +; CHECK-NEXT: br label [[BB2:%.*]] +; CHECK: bb2: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB:%.*]] ], [ [[ADD:%.*]], [[BB2]] ] +; CHECK-NEXT: [[PHI3:%.*]] = phi ptr [ [[ARG1:%.*]], [[BB]] ], [ [[GETELEMENTPTR:%.*]], [[BB2]] ] +; CHECK-NEXT: [[GETELEMENTPTR]] = getelementptr i8, ptr [[PHI3]], i64 [[ARG:%.*]] +; CHECK-NEXT: [[ADD]] = add i32 [[PHI]], 1 +; CHECK-NEXT: br label [[BB2]] +; +bb: + br label %bb2 +bb2: ; preds = %bb2, %bb + %phi = phi i32 [ 0, %bb ], [ %add, %bb2 ] + %phi3 = phi ptr [ %arg1, %bb ], [ %getelementptr, %bb2 ] + %getelementptr = getelementptr i8, ptr %phi3, i64 %arg + %add = add i32 %phi, 1 + br label %bb2 +} diff --git a/llvm/test/Transforms/ConstraintElimination/or.ll b/llvm/test/Transforms/ConstraintElimination/or.ll index 01b8ca973efa5..084f6e0f7f19c 100644 --- a/llvm/test/Transforms/ConstraintElimination/or.ll +++ b/llvm/test/Transforms/ConstraintElimination/or.ll @@ -3,7 +3,7 @@ declare void @use(i1) -define void @test_or_ule(i4 %x, i4 %y, i4 %z, i4 %a) { +define i1 @test_or_ule(i4 %x, i4 %y, i4 %z, i4 %a) { ; CHECK-LABEL: @test_or_ule( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]] @@ -12,18 +12,16 @@ define void @test_or_ule(i4 %x, i4 %y, i4 %z, i4 %a) { ; CHECK-NEXT: br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: ; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 [[X]], [[Z]] -; CHECK-NEXT: call void @use(i1 [[C_3]]) ; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 [[X]], [[A:%.*]] -; CHECK-NEXT: call void @use(i1 [[C_4]]) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[R_1:%.*]] = xor i1 [[C_3]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_1]] ; CHECK: exit: -; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[A]] -; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[R_2:%.*]] = xor i1 false, [[C_5]] +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], true +; CHECK-NEXT: [[R_4:%.*]] = xor i1 [[R_3]], true +; CHECK-NEXT: [[R_5:%.*]] = xor i1 [[R_4]], true +; CHECK-NEXT: ret i1 [[R_5]] ; entry: %c.1 = icmp ule i4 %x, %y @@ -33,34 +31,30 @@ entry: bb1: %c.3 = icmp ule i4 %x, %z - call void @use(i1 %c.3) - %c.4 = icmp ule i4 %x, %a - call void @use(i1 %c.4) + %r.1 = xor i1 %c.3, %c.4 - ret void + ret i1 %r.1 exit: %f.1 = icmp ule i4 %x, %z - call void @use(i1 %f.1) - %c.5 = icmp ule i4 %x, %a - call void @use(i1 %c.5) + %r.2 = xor i1 %f.1, %c.5 %t.1 = icmp ugt i4 %y, %z - call void @use(i1 %t.1) + %r.3 = xor i1 %r.2, %t.1 %t.2 = icmp ugt i4 %x, %y - call void @use(i1 %t.2) + %r.4 = xor i1 %r.3, %t.2 %t.3 = icmp ugt i4 %x, %z - call void @use(i1 %t.3) + %r.5 = xor i1 %r.4, %t.3 - ret void + ret i1 %r.5 } ; The result of test_or_ule and test_or_select_ule should be same -define void @test_or_select_ule(i4 %x, i4 %y, i4 %z, i4 %a) { +define i1 @test_or_select_ule(i4 %x, i4 %y, i4 %z, i4 %a) { ; CHECK-LABEL: @test_or_select_ule( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]] @@ -69,18 +63,16 @@ define void @test_or_select_ule(i4 %x, i4 %y, i4 %z, i4 %a) { ; CHECK-NEXT: br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: ; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 [[X]], [[Z]] -; CHECK-NEXT: call void @use(i1 [[C_3]]) ; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 [[X]], [[A:%.*]] -; CHECK-NEXT: call void @use(i1 [[C_4]]) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[R_1:%.*]] = xor i1 [[C_3]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_1]] ; CHECK: exit: -; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[A]] -; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[R_2:%.*]] = xor i1 false, [[C_5]] +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], true +; CHECK-NEXT: [[R_4:%.*]] = xor i1 [[R_3]], true +; CHECK-NEXT: [[R_5:%.*]] = xor i1 [[R_4]], true +; CHECK-NEXT: ret i1 [[R_5]] ; entry: %c.1 = icmp ule i4 %x, %y @@ -90,30 +82,25 @@ entry: bb1: %c.3 = icmp ule i4 %x, %z - call void @use(i1 %c.3) - %c.4 = icmp ule i4 %x, %a - call void @use(i1 %c.4) - - ret void + %r.1 = xor i1 %c.3, %c.4 + ret i1 %r.1 exit: %f.1 = icmp ule i4 %x, %z - call void @use(i1 %f.1) - %c.5 = icmp ule i4 %x, %a - call void @use(i1 %c.5) + %r.2 = xor i1 %f.1, %c.5 %t.1 = icmp ugt i4 %y, %z - call void @use(i1 %t.1) + %r.3 = xor i1 %r.2, %t.1 %t.2 = icmp ugt i4 %x, %y - call void @use(i1 %t.2) + %r.4 = xor i1 %r.3, %t.2 %t.3 = icmp ugt i4 %x, %z - call void @use(i1 %t.3) + %r.5 = xor i1 %r.4, %t.3 - ret void + ret i1 %r.5 } define i1 @test_or_chain_ule_1(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) { diff --git a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll index 63e1826ece5d7..09165bd1048e6 100644 --- a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll +++ b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll @@ -1,5 +1,8 @@ ; RUN: opt -passes=constraint-elimination -constraint-elimination-dump-reproducers -pass-remarks=constraint-elimination %s 2>&1 | FileCheck %s +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" declare void @use(i1) @@ -166,7 +169,16 @@ declare void @noundef(ptr noundef) ; Currently this fails decomposition. No reproducer should be generated. define i1 @test_inbounds_precondition(ptr %src, i32 %n, i32 %idx) { -; CHECK-NOT: test_inbounds_precondition +; CHECK-LABEL: define i1 @"{{.*}}test_inbounds_preconditionrepro"(i64 %ub, ptr %src) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = icmp uge i64 %ub, 20 +; CHECK-NEXT: call void @llvm.assume(i1 %0) +; CHECK-NEXT: %upper = getelementptr inbounds i32, ptr %src, i64 5 +; CHECK-NEXT: %src.idx.4 = getelementptr i32, ptr %src, i64 4 +; CHECK-NEXT: %cmp.upper.4 = icmp ule ptr %src.idx.4, %upper +; CHECK-NEXT: ret i1 %cmp.upper.4 +; CHECK-NEXT: } +; entry: %upper = getelementptr inbounds i32, ptr %src, i64 5 %src.idx.4 = getelementptr i32, ptr %src, i64 4 diff --git a/llvm/test/Transforms/ConstraintElimination/sext-unsigned-predicates.ll b/llvm/test/Transforms/ConstraintElimination/sext-unsigned-predicates.ll index 00dc48ef89c99..fee057d95b929 100644 --- a/llvm/test/Transforms/ConstraintElimination/sext-unsigned-predicates.ll +++ b/llvm/test/Transforms/ConstraintElimination/sext-unsigned-predicates.ll @@ -13,8 +13,7 @@ define void @uge_sext(i16 %x, i32 %y) { ; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_1]], [[C_2]] ; CHECK-NEXT: br i1 [[AND]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[T_1:%.*]] = icmp uge i32 [[X_EXT]], [[Y]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[C_3:%.*]] = icmp uge i16 [[X]], -10 ; CHECK-NEXT: call void @use(i1 [[C_3]]) ; CHECK-NEXT: [[C_4:%.*]] = icmp uge i32 [[X_EXT]], -9 diff --git a/llvm/test/Transforms/ConstraintElimination/sge.ll b/llvm/test/Transforms/ConstraintElimination/sge.ll index d02006b9cd28f..b208e4681e3d4 100644 --- a/llvm/test/Transforms/ConstraintElimination/sge.ll +++ b/llvm/test/Transforms/ConstraintElimination/sge.ll @@ -3,242 +3,344 @@ declare void @use(i1) -define void @test_1_variable_constraint(i32 %x, i32 %y, i32 %z) { +define i1 @test_1_variable_constraint(i8 %x, i8 %y, i8 %z) { ; CHECK-LABEL: @test_1_variable_constraint( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_2]]) -; CHECK-NEXT: [[C_3:%.*]] = icmp sge i32 [[Y]], [[X]] -; CHECK-NEXT: call void @use(i1 [[C_3]]) -; CHECK-NEXT: [[C_4:%.*]] = icmp sge i32 10, [[X]] -; CHECK-NEXT: call void @use(i1 [[C_4]]) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[X]], 10 +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, [[C_2]] +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[Y]], [[X]] +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_3]] +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 10, [[X]] +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_3]] ; CHECK: bb2: -; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 false) -; CHECK-NEXT: [[C_5:%.*]] = icmp sge i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: [[C_6:%.*]] = icmp sge i32 10, [[X]] -; CHECK-NEXT: call void @use(i1 [[C_6]]) -; CHECK-NEXT: ret void +; CHECK-NEXT: [[R_4:%.*]] = xor i1 true, false +; CHECK-NEXT: [[C_5:%.*]] = icmp sge i8 [[X]], 10 +; CHECK-NEXT: [[R_5:%.*]] = xor i1 [[R_4]], [[C_5]] +; CHECK-NEXT: [[C_6:%.*]] = icmp sge i8 10, [[X]] +; CHECK-NEXT: [[R_6:%.*]] = xor i1 [[R_5]], [[C_6]] +; CHECK-NEXT: ret i1 [[R_6]] ; entry: - %c.1 = icmp sge i32 %x, %y + %c.1 = icmp sge i8 %x, %y br i1 %c.1, label %bb1, label %bb2 bb1: - %t.1 = icmp sge i32 %x, %y - call void @use(i1 %t.1) - %c.2 = icmp sge i32 %x, 10 - call void @use(i1 %c.2) - %c.3 = icmp sge i32 %y, %x - call void @use(i1 %c.3) - %c.4 = icmp sge i32 10, %x - call void @use(i1 %c.4) - ret void + %t.1 = icmp sge i8 %x, %y + %c.2 = icmp sge i8 %x, 10 + %r.1 = xor i1 %t.1, %c.2 + + %c.3 = icmp sge i8 %y, %x + %r.2 = xor i1 %r.1, %c.3 + + %c.4 = icmp sge i8 10, %x + %r.3 = xor i1 %r.2, %c.4 + ret i1 %r.3 bb2: - %t.2 = icmp sge i32 %y, %x - call void @use(i1 %t.2) - %f.1 = icmp sge i32 %x, %y - call void @use(i1 %f.1) - %c.5 = icmp sge i32 %x, 10 - call void @use(i1 %c.5) - %c.6 = icmp sge i32 10, %x - call void @use(i1 %c.6) - ret void + %t.2 = icmp sge i8 %y, %x + %f.1 = icmp sge i8 %x, %y + %r.4 = xor i1 %t.2, %f.1 + + %c.5 = icmp sge i8 %x, 10 + %r.5 = xor i1 %r.4, %c.5 + + %c.6 = icmp sge i8 10, %x + %r.6 = xor i1 %r.5, %c.6 + ret i1 %r.6 } -define void @test_1_constant_constraint(i32 %x) { +define void @test_1_constant_constraint(i8 %x) { ; CHECK-LABEL: @test_1_constant_constraint( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], 10 +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], 10 ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: ; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[X]], 11 +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[X]], 11 ; CHECK-NEXT: call void @use(i1 [[C_2]]) -; CHECK-NEXT: [[C_4:%.*]] = icmp sge i32 10, [[X]] +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 10, [[X]] ; CHECK-NEXT: call void @use(i1 [[C_4]]) ; CHECK-NEXT: ret void ; CHECK: bb2: ; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: call void @use(i1 false) -; CHECK-NEXT: [[C_5:%.*]] = icmp sge i32 [[X]], 9 +; CHECK-NEXT: [[C_5:%.*]] = icmp sge i8 [[X]], 9 ; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: [[C_6:%.*]] = icmp sge i32 1, [[X]] +; CHECK-NEXT: [[C_6:%.*]] = icmp sge i8 1, [[X]] ; CHECK-NEXT: call void @use(i1 [[C_6]]) ; CHECK-NEXT: ret void ; entry: - %c.1 = icmp sge i32 %x, 10 + %c.1 = icmp sge i8 %x, 10 br i1 %c.1, label %bb1, label %bb2 bb1: - %t.1 = icmp sge i32 %x, 10 + %t.1 = icmp sge i8 %x, 10 call void @use(i1 %t.1) - %t.2 = icmp sge i32 %x, 9 + %t.2 = icmp sge i8 %x, 9 call void @use(i1 %t.2) - %c.2 = icmp sge i32 %x, 11 + %c.2 = icmp sge i8 %x, 11 call void @use(i1 %c.2) - %c.4 = icmp sge i32 10, %x + %c.4 = icmp sge i8 10, %x call void @use(i1 %c.4) ret void bb2: - %t.3 = icmp sge i32 11, %x + %t.3 = icmp sge i8 11, %x call void @use(i1 %t.3) - %f.1 = icmp sge i32 %x, 10 + %f.1 = icmp sge i8 %x, 10 call void @use(i1 %f.1) - %f.1.1 = icmp sge i32 %x, 10 + %f.1.1 = icmp sge i8 %x, 10 call void @use(i1 %f.1.1) - %c.5 = icmp sge i32 %x, 9 + %c.5 = icmp sge i8 %x, 9 call void @use(i1 %c.5) - %c.6 = icmp sge i32 1, %x + %c.6 = icmp sge i8 1, %x call void @use(i1 %c.6) ret void } -define i32 @test1(i32 %x, i32 %y, i32 %z) { +define i8 @test1(i8 %x, i8 %y, i8 %z) { ; CHECK-LABEL: @test1( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[Y]], [[Z:%.*]] ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: ; CHECK-NEXT: br i1 true, label [[BB3:%.*]], label [[EXIT]] ; CHECK: bb3: -; CHECK-NEXT: ret i32 10 +; CHECK-NEXT: ret i8 10 ; CHECK: exit: -; CHECK-NEXT: ret i32 20 +; CHECK-NEXT: ret i8 20 ; entry: - %c.1 = icmp sge i32 %x, %y + %c.1 = icmp sge i8 %x, %y br i1 %c.1, label %bb1, label %exit bb1: - %c.2 = icmp sge i32 %y, %z + %c.2 = icmp sge i8 %y, %z br i1 %c.2, label %bb2, label %exit bb2: - %c.3 = icmp sge i32 %x, %z + %c.3 = icmp sge i8 %x, %z br i1 %c.3, label %bb3, label %exit bb3: - ret i32 10 + ret i8 10 exit: - ret i32 20 + ret i8 20 } -define i32 @test2(i32 %x, i32 %y, i32 %z, i32 %a) { +define i8 @test2(i8 %x, i8 %y, i8 %z, i8 %a) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[Y]], [[Z:%.*]] ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: -; CHECK-NEXT: [[C_3:%.*]] = icmp sge i32 [[X]], [[A:%.*]] +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[X]], [[A:%.*]] ; CHECK-NEXT: br i1 [[C_3]], label [[BB3:%.*]], label [[EXIT]] ; CHECK: bb3: -; CHECK-NEXT: ret i32 10 +; CHECK-NEXT: ret i8 10 ; CHECK: exit: -; CHECK-NEXT: ret i32 20 +; CHECK-NEXT: ret i8 20 ; entry: - %c.1 = icmp sge i32 %x, %y + %c.1 = icmp sge i8 %x, %y br i1 %c.1, label %bb1, label %exit bb1: - %c.2 = icmp sge i32 %y, %z + %c.2 = icmp sge i8 %y, %z br i1 %c.2, label %bb2, label %exit bb2: - %c.3 = icmp sge i32 %x, %a + %c.3 = icmp sge i8 %x, %a br i1 %c.3, label %bb3, label %exit bb3: - ret i32 10 + ret i8 10 exit: - ret i32 20 + ret i8 20 } -define i32 @test3(i32 %x, i32 %y) { +define i8 @test3(i8 %x, i8 %y) { ; CHECK-LABEL: @test3( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], 10 +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], 10 ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[Y:%.*]], 20 +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[Y:%.*]], 20 ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: -; CHECK-NEXT: ret i32 10 +; CHECK-NEXT: ret i8 10 ; CHECK: exit: -; CHECK-NEXT: ret i32 20 +; CHECK-NEXT: ret i8 20 ; entry: - %c.1 = icmp sge i32 %x, 10 + %c.1 = icmp sge i8 %x, 10 br i1 %c.1, label %bb1, label %exit bb1: - %c.2 = icmp sge i32 %y, 20 + %c.2 = icmp sge i8 %y, 20 br i1 %c.2, label %bb2, label %exit bb2: - ret i32 10 + ret i8 10 exit: - ret i32 20 + ret i8 20 } -define i32 @test4(i32 %x, i32 %y, i32 %z) { +define i8 @test4(i8 %x, i8 %y, i8 %z) { ; CHECK-LABEL: @test4( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp sge i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[Y]], [[Z:%.*]] ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: [[U_1:%.*]] = icmp eq i32 [[X]], [[Z]] +; CHECK-NEXT: [[U_1:%.*]] = icmp eq i8 [[X]], [[Z]] ; CHECK-NEXT: call void @use(i1 [[U_1]]) -; CHECK-NEXT: ret i32 10 +; CHECK-NEXT: ret i8 10 ; CHECK: exit: -; CHECK-NEXT: ret i32 20 +; CHECK-NEXT: ret i8 20 ; entry: - %c.1 = icmp sge i32 %x, %y + %c.1 = icmp sge i8 %x, %y br i1 %c.1, label %bb1, label %exit bb1: - %c.2 = icmp sge i32 %y, %z + %c.2 = icmp sge i8 %y, %z br i1 %c.2, label %bb2, label %exit bb2: - %t.1 = icmp sge i32 %x, %z + %t.1 = icmp sge i8 %x, %z call void @use(i1 %t.1) - %u.1 = icmp eq i32 %x, %z + %u.1 = icmp eq i8 %x, %z call void @use(i1 %u.1) - ret i32 10 + ret i8 10 exit: - ret i32 20 + ret i8 20 +} + + +define i1 @test_1_variable_constraint_negative_constants(i8 %x) { +; CHECK-LABEL: @test_1_variable_constraint_negative_constants( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], -10 +; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, true +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[X]], -9 +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_2]] +; CHECK-NEXT: ret i1 [[R_2]] +; CHECK: bb2: +; CHECK-NEXT: [[R_3:%.*]] = xor i1 false, true +; CHECK-NEXT: [[R_4:%.*]] = xor i1 [[R_3]], true +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 [[X]], -11 +; CHECK-NEXT: [[R_5:%.*]] = xor i1 [[R_4]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_5]] +; +entry: + %c.1 = icmp sge i8 %x, -10 + br i1 %c.1, label %bb1, label %bb2 + +bb1: + %t.1 = icmp sge i8 %x, -11 + %t.2 = icmp sge i8 %x, -10 + %r.1 = xor i1 %t.1, %t.2 + + %c.2 = icmp sge i8 %x, -9 + %r.2 = xor i1 %r.1, %c.2 + ret i1 %r.2 + +bb2: + %f.1 = icmp sge i8 %x, -10 + %t.3 = icmp slt i8 %x, -10 + %r.3 = xor i1 %f.1, %t.3 + + %t.4 = icmp slt i8 %x, -9 + %r.4 = xor i1 %r.3, %t.4 + + %c.4 = icmp sge i8 %x, -11 + %r.5 = xor i1 %r.4, %c.4 + ret i1 %r.5 +} + +define i1 @test_1_variable_2_constraints_negative_constants(i8 %x, i8 %y) { +; CHECK-LABEL: @test_1_variable_2_constraints_negative_constants( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[X]], -10 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_1]], [[C_2]] +; CHECK-NEXT: br i1 [[AND]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, true +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], true +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[X]], -9 +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_3]] +; CHECK-NEXT: [[R_4:%.*]] = xor i1 [[R_3]], true +; CHECK-NEXT: [[T_5:%.*]] = icmp sge i8 [[Y]], -9 +; CHECK-NEXT: [[R_5:%.*]] = xor i1 [[R_4]], [[T_5]] +; CHECK-NEXT: ret i1 [[R_5]] +; CHECK: bb2: +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 [[Y]], [[X]] +; CHECK-NEXT: [[C_5:%.*]] = icmp sge i8 [[X]], [[Y]] +; CHECK-NEXT: [[R_6:%.*]] = xor i1 [[C_4]], [[C_5]] +; CHECK-NEXT: [[C_6:%.*]] = icmp sge i8 [[X]], -11 +; CHECK-NEXT: [[R_7:%.*]] = xor i1 [[R_6]], [[C_5]] +; CHECK-NEXT: ret i1 [[R_7]] +; +entry: + %c.1 = icmp sge i8 %x, %y + %c.2 = icmp sge i8 %x, -10 + %and = and i1 %c.1, %c.2 + br i1 %and, label %bb1, label %bb2 + +bb1: + %t.1 = icmp sge i8 %x, %y + %t.2 = icmp sge i8 %x, -10 + %r.1 = xor i1 %t.1, %t.2 + + %t.3 = icmp sge i8 %x, -10 + %r.2 = xor i1 %r.1, %t.3 + + %c.3 = icmp sge i8 %x, -9 + %r.3 = xor i1 %r.2, %c.3 + + %t.4 = icmp sge i8 %x, -11 + %r.4 = xor i1 %r.3, %t.4 + + %t.5 = icmp sge i8 %y, -9 + %r.5 = xor i1 %r.4, %t.5 + ret i1 %r.5 + +bb2: + %c.4 = icmp sge i8 %y, %x + %c.5 = icmp sge i8 %x, %y + %r.6 = xor i1 %c.4, %c.5 + + %c.6 = icmp sge i8 %x, -11 + %r.7 = xor i1 %r.6, %c.5 + ret i1 %r.7 } define i1 @sge_sgt(i32 %x, i32 %y, i32 %z) { diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll index efb218ff099fb..fec03ea148ba9 100644 --- a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll +++ b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll @@ -65,8 +65,7 @@ define void @iv_known_non_negative_constant_trip_count_no_nsw_flag(i8 %N) { ; CHECK-NEXT: call void @use(i1 [[F_1]]) ; CHECK-NEXT: [[F_2:%.*]] = icmp sle i8 [[N]], [[IV]] ; CHECK-NEXT: call void @use(i1 [[F_2]]) -; CHECK-NEXT: [[C_0:%.*]] = icmp ugt i8 [[IV]], 2 -; CHECK-NEXT: call void @use(i1 [[C_0]]) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[IV_NEXT]] = add nuw i8 [[IV]], 1 ; CHECK-NEXT: br label [[LOOP_HEADER]] ; CHECK: exit.1: @@ -153,11 +152,9 @@ define void @iv_may_signed_wrap_variable_trip_count(i8 %N) { ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[IV]], [[N:%.*]] ; CHECK-NEXT: br i1 [[CMP]], label [[LOOP_LATCH]], label [[EXIT_1:%.*]] ; CHECK: loop.latch: -; CHECK-NEXT: [[T_1:%.*]] = icmp ugt i8 [[N]], [[IV]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: [[F_1:%.*]] = icmp ule i8 [[N]], [[IV]] -; CHECK-NEXT: call void @use(i1 [[F_1]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_0:%.*]] = icmp ugt i8 [[IV]], 2 ; CHECK-NEXT: call void @use(i1 [[C_0]]) diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned.ll b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned.ll index 68e48c7d2944b..cca7534b73ad8 100644 --- a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned.ll +++ b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned.ll @@ -287,6 +287,8 @@ else: ret i1 1 } +; Even though %cnt is not known signed positive %cmp can be simplified +; because %add.ptr uses it zero-extended. define i1 @cnt_not_known_positive_sgt_against_base_with_zext(ptr %p, i32 %cnt) { ; CHECK-LABEL: @cnt_not_known_positive_sgt_against_base_with_zext( ; CHECK-NEXT: entry: @@ -315,6 +317,8 @@ else: ret i1 1 } +; Even though %cnt is not known signed positive %cmp can be simplified +; because %add.ptr uses it zero-extended. define i1 @cnt_not_known_positive_sge_against_base_with_zext(ptr %p, i32 %cnt) { ; CHECK-LABEL: @cnt_not_known_positive_sge_against_base_with_zext( ; CHECK-NEXT: entry: diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-unsigned-facts-to-signed-is-known-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/transfer-unsigned-facts-to-signed-is-known-non-negative.ll index b127361ff0244..7e34f2f6fac86 100644 --- a/llvm/test/Transforms/ConstraintElimination/transfer-unsigned-facts-to-signed-is-known-non-negative.ll +++ b/llvm/test/Transforms/ConstraintElimination/transfer-unsigned-facts-to-signed-is-known-non-negative.ll @@ -9,13 +9,12 @@ define void @iv_known_non_negative_iv_constant_trip_count_uge() { ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] ; CHECK: loop.header: ; CHECK-NEXT: [[IV:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] -; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[IV]], 2 -; CHECK-NEXT: br i1 [[CMP]], label [[LOOP_LATCH]], label [[EXIT_1:%.*]] +; CHECK-NEXT: br i1 false, label [[LOOP_LATCH]], label [[EXIT_1:%.*]] ; CHECK: loop.latch: ; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: call void @use(i1 true) -; CHECK-NEXT: call void @use(i1 false) -; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1 ; CHECK-NEXT: br label [[LOOP_HEADER]] ; CHECK: exit.1: diff --git a/llvm/test/Transforms/ConstraintElimination/uses-in-different-blocks.ll b/llvm/test/Transforms/ConstraintElimination/uses-in-different-blocks.ll index 6a2fb027e3235..6f0295dcada1a 100644 --- a/llvm/test/Transforms/ConstraintElimination/uses-in-different-blocks.ll +++ b/llvm/test/Transforms/ConstraintElimination/uses-in-different-blocks.ll @@ -25,7 +25,6 @@ else: ret i1 %c.2 } - define i1 @test_conds_single_use_in_different_blocks_2(i8 %x, i8 %y) { ; CHECK-LABEL: @test_conds_single_use_in_different_blocks_2( ; CHECK-NEXT: entry: diff --git a/llvm/test/Transforms/GVN/noalias.ll b/llvm/test/Transforms/GVN/noalias.ll index 931564db47b39..98cc930fa2df9 100644 --- a/llvm/test/Transforms/GVN/noalias.ll +++ b/llvm/test/Transforms/GVN/noalias.ll @@ -2,8 +2,7 @@ define i32 @test1(ptr %p, ptr %q) { ; CHECK-LABEL: @test1(ptr %p, ptr %q) -; CHECK: load i32, ptr %p -; CHECK-NOT: noalias +; CHECK: load i32, ptr %p, align 4, !noalias ![[SCOPE1:[0-9]+]] ; CHECK: %c = add i32 %a, %a %a = load i32, ptr %p, !noalias !3 %b = load i32, ptr %p @@ -13,7 +12,7 @@ define i32 @test1(ptr %p, ptr %q) { define i32 @test2(ptr %p, ptr %q) { ; CHECK-LABEL: @test2(ptr %p, ptr %q) -; CHECK: load i32, ptr %p, align 4, !alias.scope ![[SCOPE1:[0-9]+]] +; CHECK: load i32, ptr %p, align 4, !alias.scope ![[SCOPE1]] ; CHECK: %c = add i32 %a, %a %a = load i32, ptr %p, !alias.scope !3 %b = load i32, ptr %p, !alias.scope !3 @@ -32,7 +31,7 @@ define i32 @test3(ptr %p, ptr %q) { } ; CHECK: ![[SCOPE1]] = !{!{{[0-9]+}}} -; CHECK: ![[SCOPE2]] = !{!{{[0-9]+}}, !{{[0-9]+}}} +; CHECK: ![[SCOPE2]] = !{!{{[0-9]+}}} declare i32 @foo(ptr) readonly !0 = distinct !{!0, !2, !"callee0: %a"} diff --git a/llvm/test/Transforms/GVN/tbaa.ll b/llvm/test/Transforms/GVN/tbaa.ll index 46d1bb737a693..b5dd3867bdbc2 100644 --- a/llvm/test/Transforms/GVN/tbaa.ll +++ b/llvm/test/Transforms/GVN/tbaa.ll @@ -1,10 +1,13 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=gvn -S < %s | FileCheck %s define i32 @test1(ptr %p, ptr %q) { -; CHECK-LABEL: @test1(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p) -; CHECK-NOT: tbaa -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test1( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA0:![0-9]+]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !0 %b = call i32 @foo(ptr %p) %c = add i32 %a, %b @@ -12,9 +15,12 @@ define i32 @test1(ptr %p, ptr %q) { } define i32 @test2(ptr %p, ptr %q) { -; CHECK-LABEL: @test2(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAGC:!.*]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test2( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA0]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !0 %b = call i32 @foo(ptr %p), !tbaa !0 %c = add i32 %a, %b @@ -22,9 +28,12 @@ define i32 @test2(ptr %p, ptr %q) { } define i32 @test3(ptr %p, ptr %q) { -; CHECK-LABEL: @test3(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAGB:!.*]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test3( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA4:![0-9]+]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !3 %b = call i32 @foo(ptr %p), !tbaa !3 %c = add i32 %a, %b @@ -32,9 +41,12 @@ define i32 @test3(ptr %p, ptr %q) { } define i32 @test4(ptr %p, ptr %q) { -; CHECK-LABEL: @test4(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAGA:!.*]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test4( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA6:![0-9]+]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !1 %b = call i32 @foo(ptr %p), !tbaa !0 %c = add i32 %a, %b @@ -42,9 +54,12 @@ define i32 @test4(ptr %p, ptr %q) { } define i32 @test5(ptr %p, ptr %q) { -; CHECK-LABEL: @test5(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAGA]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test5( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA0]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !0 %b = call i32 @foo(ptr %p), !tbaa !1 %c = add i32 %a, %b @@ -52,9 +67,12 @@ define i32 @test5(ptr %p, ptr %q) { } define i32 @test6(ptr %p, ptr %q) { -; CHECK-LABEL: @test6(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAGA]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test6( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA0]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !0 %b = call i32 @foo(ptr %p), !tbaa !3 %c = add i32 %a, %b @@ -62,10 +80,12 @@ define i32 @test6(ptr %p, ptr %q) { } define i32 @test7(ptr %p, ptr %q) { -; CHECK-LABEL: @test7(ptr %p, ptr %q) -; CHECK: call i32 @foo(ptr %p) -; CHECK-NOT: tbaa -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test7( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA7:![0-9]+]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !4 %b = call i32 @foo(ptr %p), !tbaa !3 %c = add i32 %a, %b @@ -73,9 +93,11 @@ define i32 @test7(ptr %p, ptr %q) { } define i32 @test8(ptr %p, ptr %q) { -; CHECK-LABEL: @test8 -; CHECK-NEXT: store i32 15, ptr %p -; CHECK-NEXT: ret i32 0 +; CHECK-LABEL: define i32 @test8( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: store i32 15, ptr [[P]], align 4 +; CHECK-NEXT: ret i32 0 +; ; Since we know the location is invariant, we can forward the ; load across the potentially aliasing store. @@ -87,9 +109,11 @@ define i32 @test8(ptr %p, ptr %q) { } define i32 @test9(ptr %p, ptr %q) { -; CHECK-LABEL: @test9 -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret i32 0 +; CHECK-LABEL: define i32 @test9( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: call void @clobber() +; CHECK-NEXT: ret i32 0 +; ; Since we know the location is invariant, we can forward the ; load across the potentially aliasing store (within the call). @@ -103,9 +127,12 @@ define i32 @test9(ptr %p, ptr %q) { define i32 @test10(ptr %p, ptr %q) { ; If one access encloses the other, then the merged access is the enclosed one ; and not just the common final access type. -; CHECK-LABEL: @test10 -; CHECK: call i32 @foo(ptr %p), !tbaa [[TAG_X_i:!.*]] -; CHECK: %c = add i32 %a, %a +; CHECK-LABEL: define i32 @test10( +; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = call i32 @foo(ptr [[P]]), !tbaa [[TBAA10:![0-9]+]] +; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]] +; CHECK-NEXT: ret i32 [[C]] +; %a = call i32 @foo(ptr %p), !tbaa !15 ; TAG_X_i %b = call i32 @foo(ptr %p), !tbaa !19 ; TAG_Y_x_i %c = add i32 %a, %b @@ -115,12 +142,6 @@ define i32 @test10(ptr %p, ptr %q) { declare void @clobber() declare i32 @foo(ptr) readonly -; CHECK-DAG: [[TAGC]] = !{[[TYPEC:!.*]], [[TYPEC]], i64 0} -; CHECK-DAG: [[TYPEC]] = !{!"C", [[TYPEA:!.*]]} -; CHECK-DAG: [[TYPEA]] = !{!"A", !{{.*}}} -; CHECK-DAG: [[TAGB]] = !{[[TYPEB:!.*]], [[TYPEB]], i64 0} -; CHECK-DAG: [[TYPEB]] = !{!"B", [[TYPEA]]} -; CHECK-DAG: [[TAGA]] = !{[[TYPEA]], [[TYPEA]], i64 0} !0 = !{!5, !5, i64 0} !1 = !{!6, !6, i64 0} !2 = !{!"tbaa root"} @@ -132,9 +153,6 @@ declare i32 @foo(ptr) readonly !8 = !{!"another root"} !11 = !{!"scalar type", !8} -; CHECK-DAG: [[TAG_X_i]] = !{[[TYPE_X:!.*]], [[TYPE_int:!.*]], i64 0} -; CHECK-DAG: [[TYPE_X:!.*]] = !{!"struct X", [[TYPE_int]], i64 0} -; CHECK-DAG: [[TYPE_int]] = !{!"int", {{!.*}}, i64 0} !15 = !{!16, !17, i64 0} ; TAG_X_i !16 = !{!"struct X", !17, i64 0} ; struct X { int i; }; !17 = !{!"int", !18, i64 0} @@ -146,3 +164,19 @@ declare i32 @foo(ptr) readonly ; A TBAA structure who's only point is to have a constant location. !9 = !{!"yet another root"} !10 = !{!"node", !9, i64 1} +;. +; CHECK: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0} +; CHECK: [[META1]] = !{!"C", [[META2:![0-9]+]]} +; CHECK: [[META2]] = !{!"A", [[META3:![0-9]+]]} +; CHECK: [[META3]] = !{!"tbaa root"} +; CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +; CHECK: [[META5]] = !{!"B", [[META2]]} +; CHECK: [[TBAA6]] = !{[[META2]], [[META2]], i64 0} +; CHECK: [[TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +; CHECK: [[META8]] = !{!"scalar type", [[META9:![0-9]+]]} +; CHECK: [[META9]] = !{!"another root"} +; CHECK: [[TBAA10]] = !{[[META11:![0-9]+]], [[META12:![0-9]+]], i64 0} +; CHECK: [[META11]] = !{!"struct X", [[META12]], i64 0} +; CHECK: [[META12]] = !{!"int", [[META13:![0-9]+]], i64 0} +; CHECK: [[META13]] = !{!"char", [[META3]], i64 0} +;. diff --git a/llvm/test/Transforms/HotColdSplit/no-outline-bounds-safety-traps.ll b/llvm/test/Transforms/HotColdSplit/no-outline-bounds-safety-traps.ll new file mode 100644 index 0000000000000..148bb042dc384 --- /dev/null +++ b/llvm/test/Transforms/HotColdSplit/no-outline-bounds-safety-traps.ll @@ -0,0 +1,44 @@ +; RUN: opt -passes=hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.14.0" + +declare void @_Z10sideeffectv() +declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind + +; Don't outline -fbounds-safety traps + +; CHECK-LABEL: define {{.*}}@foo( +; CHECK-NOT: foo.cold.1 +define void @foo(i32, ptr) { + %3 = icmp eq i32 %0, 0 + tail call void @_Z10sideeffectv() + br i1 %3, label %5, label %4 + +;